【问题标题】:How to prevent rerenders of whole components in React?如何防止在 React 中重新渲染整个组件?
【发布时间】:2020-03-20 10:04:17
【问题描述】:

我有一个模态组件,它是 3 个 Tab 组件的父组件。 formData 是 Modal 组件状态的一部分,并将其传递给它的子组件。问题是,每当任何选项卡中的任何字段发生变化时,都会触发整个 Modal 组件及其子组件的重新渲染,从而使表单有点滞后。

我的印象是 React 只会重新渲染实际 DOM 和虚拟 DOM 之间存在的任何差异?有没有办法解决这个问题?

Codesandbox 最小示例

【问题讨论】:

  • 如果你有一个功能组件,使用React.memo 使其成为纯组件,如果你有一个类组件,则从 PureComponent 继承。如果您不更改道具,那么组件将不会呈现,如果您确实更改了道具,那么组件无论如何都应该呈现。如果您从父级传递带有prop={{new:object}}prop={new=>reference} 的道具,那么无论您做什么,孩子都会渲染,因为您每次都传递一个新的道具参考

标签: javascript reactjs


【解决方案1】:

我已对您的代码进行了一些更改,因此当详细信息更改时联系人不会重新呈现,反之亦然。

//Wrap functional component Details in React.memo
const Details = React.memo(function Details({
  formData: { title, description },
  onChange,
}) {
  console.log('render details');
  return (
    <React.Fragment>
      <input
        value={title}
        onChange={({ target: { value } }) =>
          onChange(value, 'details', 'title')
        }
      />
      <input
        value={description}
        onChange={({ target: { value } }) =>
          onChange(value, 'details', 'description')
        }
      />
    </React.Fragment>
  );
});
//Make Contact also pure with React.memo
const Contact = React.memo(function Contact({
  formData: { name, number },
  onChange,
}) {
  console.log('render contact');
  return (
    <React.Fragment>
      <input
        value={name}
        onChange={({ target: { value } }) =>
          onChange(value, 'contact', 'name')
        }
      />
      <input
        value={number}
        onChange={({ target: { value } }) =>
          onChange(value, 'contact', 'number')
        }
      />
    </React.Fragment>
  );
});

const Create = () => {
  const [formData, setFormData] = React.useState({
    details: {
      title: '',
      description: '',
    },
    contact: {
      name: '',
      number: '',
    },
  });
  //only create onChange once (empty dependencies for useCallback)
  const onChange = React.useCallback(
    (value, tab, field) => {
      //you were mutating and not using callback for state
      //  setting causing unnecessary dependency for useCallback
      setFormData(current => ({
        ...current,
        [tab]: {
          ...current[tab],
          [field]: value,
        },
      }));
    },
    []
  );

  return (
    <React.Fragment>
      {/* instead of passing the entire formData, only pass
      the part that it actually needs so when Contact changes
      Details isn't re rendered because you're passing an object
      that has changed you pass object containing contact to details
      and object contact details to details */}
      <Details
        formData={formData.details}
        onChange={onChange}
      />
      <Contact
        formData={formData.contact}
        onChange={onChange}
      />
    </React.Fragment>
  );
};
ReactDOM.render(
  <Create />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

【讨论】:

    猜你喜欢
    • 2017-06-05
    • 1970-01-01
    • 2019-12-05
    • 1970-01-01
    • 2020-06-17
    • 2020-03-27
    • 2021-09-29
    • 1970-01-01
    • 2020-03-18
    相关资源
    最近更新 更多