防止在功能道具更新时重新呈现

Mic*_*Cox 6 javascript reactjs react-hooks

我有一个包含几层子组件的表单。表单的状态保持在最高级别,而我向下传递作为更新顶层的道具的函数。唯一的问题是,当表单变得很大(您可以动态添加问题)时,每个单个组件都会在其中一个更新时重新加载。这是我的代码的简化版本(或代码框:https : //codesandbox.io/s/636xwz3rr ):

const App = () => {
  return <Form />;
}

const initialForm = {
  id: 1,
  sections: [
    {
      ordinal: 1,
      name: "Section Number One",
      questions: [
        { ordinal: 1, text: "Who?", response: "" },
        { ordinal: 2, text: "What?", response: "" },
        { ordinal: 3, text: "Where?", response: "" }
      ]
    },
    {
      ordinal: 2,
      name: "Numero dos",
      questions: [
        { ordinal: 1, text: "Who?", response: "" },
        { ordinal: 2, text: "What?", response: "" },
        { ordinal: 3, text: "Where?", response: "" }
      ]
    }
  ]
};

const Form = () => {
  const [form, setForm] = useState(initialForm);

  const updateSection = (idx, value) => {
    const { sections } = form;
    sections[idx] = value;
    setForm({ ...form, sections });
  };

  return (
    <>
      {form.sections.map((section, idx) => (
        <Section
          key={section.ordinal}
          section={section}
          updateSection={value => updateSection(idx, value)}
        />
      ))}
    </>
  );
};

const Section = props => {
  const { section, updateSection } = props;

  const updateQuestion = (idx, value) => {
    const { questions } = section;
    questions[idx] = value;
    updateSection({ ...section, questions });
  };

  console.log(`Rendered section "${section.name}"`);

  return (
    <>
      <div style={{ fontSize: 18, fontWeight: "bold", margin: "24px 0" }}>
        Section name:
        <input
          type="text"
          value={section.name}
          onChange={e => updateSection({ ...section, name: e.target.value })}
        />
      </div>
      <div style={{ marginLeft: 36 }}>
        {section.questions.map((question, idx) => (
          <Question
            key={question.ordinal}
            question={question}
            updateQuestion={v => updateQuestion(idx, v)}
          />
        ))}
      </div>
    </>
  );
};

const Question = props => {
  const { question, updateQuestion } = props;

  console.log(`Rendered question #${question.ordinal}`);

  return (
    <>
      <div>{question.text}</div>
      <input
        type="text"
        value={question.response}
        onChange={e =>
          updateQuestion({ ...question, response: e.target.value })
        }
      />
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

我已经尝试过使用useMemo和useCallback,但是我不知道如何使它工作。问题是传递函数以更新其父级。我无法弄清楚如何在每次表单更新时不对其进行更新。

我无法在任何地方在线找到解决方案。也许我在寻找错误的东西。感谢您提供的任何帮助!

使用Andrii-Golubenko的答案和本文以及React.memo,useCallback和useReducer的React优化,我能够提出以下解决方案:https : //codesandbox.io/s/myrjqrjm18

请注意,控制台日志仅显示重新呈现已更改组件的方式。

And*_*nko 9

  1. Use React feature React.memo for functional components to prevent re-render if props not changed, similarly to PureComponent for class components.
  2. When you pass callback like that:
<Section
    ...
    updateSection={value => updateSection(idx, value)}
/>
Run Code Online (Sandbox Code Playgroud)

your component Section will rerender each time when parent component rerender, even if other props are not changed and you use React.memo. Because your callback will re-create each time when parent component renders. You should wrap your callback in useCallback hook.

  1. Using useState is not a good decision if you need to store complex object like initialForm. It is better to use useReducer;

Here you could see working solution: https://codesandbox.io/s/o10p05m2vz