React本机在模式打开/关闭时重新发送整个组件



我的问题

我使用Array.map方法呈现一个简单项目(数字或字符串(的列表。我使用Modal添加/更新项目。然而,模态的每次打开或关闭都会使react重新呈现整个数组,即使数组保持不变。我觉得这是意料之中的行为。

问题

  1. 打开或关闭模态时,是否可以不重新发送整个组件
  2. 在不重新绘制整个列表的情况下,在数组中添加/更新新项的常见方法是什么

谢谢大家

最小代码示例

/* Console output:
* ---------------
* ROOT: render component
* -> ITEM: render 1
* -> ITEM: render 2
* -> ITEM: render 3 (not in map)
* ROOT: open modal
* ROOT: render component
* -> ITEM: render 1
* -> ITEM: render 2
* -> ITEM: render 3 (not in map)
* MODAL: close Modal
* ROOT: render component
* -> ITEM: render 1
* -> ITEM: render 2
* -> ITEM: render 3 (not in map)
*/
import * as React from 'react';
import {useState} from 'react';
import {View, Text, Modal, Pressable, StyleSheet} from 'react-native';
const items = [1, 2];
const Item = ({el}) => {
console.log(`-> ITEM: render ${el}`);
return <Text>Item: {el}</Text>;
};
const Testing = () => {
const [visible, setVisible] = useState(false);
const openModal = () => {
console.log('ROOT: open modal');
setVisible(true);
};
console.log("ROOT: render component");
return (
<View style={styles.wrapper}>
{/* Render simple list */}
<Text style={styles.header}>All items:</Text>
{items.map(el => (
<Item el={el} key={el} />
))}
<Item el={'3 (not in map)'} />
{/* Button to open modal */}
<Pressable style={styles.button} onPress={openModal}>
<Text style={styles.header}>Tap me to open modal</Text>
</Pressable>
{/*The Modal*/}
<Modal
animationType="slide"
transparent={false}
visible={visible}
onRequestClose={() => {
console.log('MODAL: close Modal');
setVisible(false);
}}>
<Text style={styles.header}>No content here...</Text>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
wrapper: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
button: {
borderRadius: 5,
backgroundColor: '#0aa',
marginVertical: 10,
padding: 10,
},
header: {
fontSize: 18,
fontWeight: '700',
},
});
export default Testing;

我今天学到了什么

一旦我们创建了一个组件,React就会跟踪它的状态,并在状态发生变化时执行重新绘制。重新评分包括该组件的所有子级。因此,我可以回答我的问题。

对问题1的回答

是的。我们需要visible不处于我们不想重新发送的组件的状态。为了实现这一点,Modal应该与我们的组件分开实现,该组件变成Modal的父组件。

同时,我们希望父级可以使集合Modal可见。正是在这里,一个对我来说新的反作用钩子出现了:useImperativeHandle

工作流程如下。父级使用封装Modal组件的forwardRef函数将引用ref转发给Modal。然后,Modal将ref声明为父级可以使用的处理程序。这个声明和可用的porperties是通过useImperativeHandler钩子提供的。就是这样。

然而,请注意,正如官方文档所说,在大多数情况下应该避免使用refs的命令式代码。

下面的代码片段是我的纪念品。现在没有关于Modal打开/关闭的重读程序!

/* Console output:
* ---------------
* ROOT: render component
* -> ITEM: render 1
* -> ITEM: render 2
* -> ITEM: render 3 (not in map)
* ROOT: open modal
* MODAL: openMe called from parent component via ref
* MODAL: close Modal
*/
//
// Modal component
//
const _MyModal = (props, ref) => {
const [visible, setVisible] = useState(false);
const openMe = () => {
console.log('MODAL: openMe called from parent component via ref');
setVisible(true);
};
useImperativeHandle(ref, () => ({publicHandler: openMe}}), [openMe]));
return (<Modal>...</Modal>);
};
const MyModal = forwardRef(_MyModal);
//
// Testing component
//
const Testing = () => {
const modalRef = useRef(null);
const openModal = () => {
console.log('ROOT: open modal');
modalRef.current.publicHandler();
};

// Rest of code without <Modal> tag
}

使用redux回答问题1

如果使用redux,则不需要使用useImperativeHandle挂钩。只需将Modal组件连接到store即可共享visible和动作创建者actSetVisible,而将父组件连接仅共享该动作创建者actSetVisible。一切都如上图所示。

但是,如果您确实想使用useImperativeHandle,那么在将Modal连接到存储时,您应该将redux指向转发引用的事实:

const MyModal = connect(mapS2P, mapD2P, null, {forwardRef: true}))(forwardRef(_MyModal));

对问题2的回答

上面的建议展示了如何在模态打开或关闭时消除过度的重新渲染。我的第二个问题暗示我在反应上犯了一些新手错误,因为我在这里真的是个新手。因此,在我看来,使用情态动词是向列表中添加元素的好方法。在我的例子中,它是一个组合组件的数组,而不是一个简单字符串的数组,重新绘制是一个关键问题。

后记

活一个世纪,学一个世纪你就会死得像个傻瓜。希望,这对任何人都有帮助。

它重新渲染的原因是因为您正在更新状态(setVisible(。

您可以使用Ref。

useState与组件生命周期直接相关;useRef不是,这意味着你可以更改它,它不会触发重新渲染。

示例:

const visible = useRef()
//your code
useEffect(() => {
visible.current = false  //initiate to false  or true 
depending on what you need.
}, [])
<Modal
animationType="slide"
transparent={false}
visible={visible}
onRequestClose={() => {
console.log('MODAL: close Modal');
visible.current = !visible.current //will toggle between true and false
}}>
<Text style={styles.header}>No content here...</Text>
</Modal>

最新更新