首页 > 解决方案 > 如何防止在 React 中重新渲染整个组件?

问题描述

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

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

Codesandbox最小示例

标签: javascriptreactjs

解决方案


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

//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>


推荐阅读