React Native 在模式打开/关闭时重新渲染整个组件

pch*_*pch 5 reactjs react-native react-modal

我的问题

我使用方法渲染简单项目(数字或字符串)的列表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;
Run Code Online (Sandbox Code Playgroud)

pch*_*pch 4

我今天学到了什么

一旦我们创建了一个组件,React就会跟踪其状态并在状态发生变化时执行重新渲染。重新渲染包括组件的所有子组件。这样,我就可以回答我的问题了。

回答问题1

是的。我们需要的组件状态visible不是我们不想重新渲染的。为了实现这一点,模态应该与我们的组件分开实现,该组件成为模态的父组件。

同时,我们希望父级可以使设置的 Modal 可见。正是在这里,一个新的 React hook 开始发挥作用:useImperativeHandle

工作流程如下。ref父级使用forwardRef包装 Modal 组件的函数转发对 Modal 的引用。然后,Modal 声明ref为可由父级使用的处理程序。该声明和可用属性是通过useImperativeHandler钩子提供的。就是这样。

但请注意,正如官方文档所述,在大多数情况下应避免使用引用的命令式代码。

下面是代码片段,作为我的纪念。此时模态打开/关闭不会重新渲染!

/* 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
}
Run Code Online (Sandbox Code Playgroud)

回答问题 1 使用redux

如果使用redux,则无需useImperativeHandle钩子。只需将 Modal 组件连接到store共享visible和动作创建者actSetVisible,而连接父组件则仅共享该动作创建者actSetVisible。一切工作原理都与上图类似。

但是,如果您确实想使用useImperativeHandle,则应指出redux在将 Modal 连接到存储时您正在转发引用:

const MyModal = connect(mapS2P, mapD2P, null, {forwardRef: true}))(forwardRef(_MyModal));
Run Code Online (Sandbox Code Playgroud)

回答问题2

上面的建议展示了如何在模式打开或关闭时摆脱过度的重新渲染。我的第二个问题暗示我在反应中做了一些新的错误,因为我真的是这里的新手。因此,以我的拙见,使用 Modals 是将元素添加到列表的好方法。就我而言,它是一个组合组件的数组,而不是一个简单字符串的数组,并且重新渲染是一个关键问题。

后记

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