【问题标题】:React - How to prevent re-rendering of all the input fields when input changesReact - 如何在输入更改时防止重新渲染所有输入字段
【发布时间】:2021-05-22 19:24:01
【问题描述】:

我正在实现一个使用 Json 生成的表单。 Json 从 API 中检索,然后循环遍历我呈现输入元素的项目。这是示例 Json:

{
  name: {
      elementType: 'input',
      label: 'Name',
      elementConfig: {
        type: 'text',
        placeholder: 'Enter name'
      },
      value: '',
      validation: {
        required: true
      },
      valid: false,
      touched: false
    }
   }

这是我呈现表单的方式:

    render() {
        const formElementsArray = [];
        for (const key in this.props.deviceConfig.sensorForm) {
         formElementsArray.push({
            id: key,
            config: this.props.deviceConfig.sensorForm[key]
         });

    const itemPerRow = 4;
      const rows = [
        ...Array(Math.ceil(props.formElementsArray.length / itemPerRow))
      ];
      const formElementRows = rows.map((row, idx) =>
        props.formElementsArray.slice(
          idx * itemPerRow,
          idx * itemPerRow + itemPerRow
        )
      );
      const content = formElementRows.map((row, idx) => (
        <div className='row' key={idx}>
          {row.map((formElement) => (
            <div className='col-md-3' key={formElement.id}>
              <Input
                key={formElement.id}
                elementType={formElement.config.elementType}
                elementConfig={formElement.config.elementConfig}
                value={formElement.config.value}
                invalid={!formElement.config.valid}
                shouldValidate={formElement.config.validation}
                touched={formElement.config.touched}
                label={formElement.config.label}
                handleChange={(event) => props.changed(event, formElement.id)}
              />
            </div>
          ))}
        </div>
 ...
    }

我将表单状态存储在 redux 中,并且在每次输入更改时,我都会更新状态。现在的问题是每次我更新状态时,整个表单都会再次重新渲染......有没有办法优化它,使得只有更新的表单元素被重新渲染?

编辑:

  1. 我在Input.js 中使用了React.memo 作为: export default React.memo(input);

  2. 我的有状态组件是纯组件。

  3. Parent 是类组件。

编辑 2:

这是我创建 formElementArray 的方法:

const formElementsArray = [];
for (const key in this.props.deviceConfig.sensorForm) {
    formElementsArray.push({
    id: key,
    config: this.props.deviceConfig.sensorForm[key]
});

【问题讨论】:

  • 我在您的 json 中没有看到 id - 有吗?
  • @WillJenkins id 是 Json 中的键名,即 name
  • name如何映射到id?它没有显示在您的问题中

标签: javascript reactjs optimization redux


【解决方案1】:

您可以像这样将内容制作为单独的组件。 并从父组件中移除 formElementsArray 属性。

export default function Content() {
  const formElementRows = useForElementRows();
      formElementRows.map((row, idx) => (
            <Input
              formId={formElement.id}
              handleChange={props.changed}
            />
      )
}

在 Input.js 中

const handleInputChange = useCallback((event) => {
   handleChange(event, formId);
}, [formId, handleChange]);
<input handleChange={handleInputChange} />
export default React.memo(Input)

这样你就可以有效地记住handleChange。这将使我们能够防止其他 &lt;Input /&gt; 的不必要渲染。 通过这样做 forElementRows 更改不会导致其他组件的任何重新渲染。

【讨论】:

  • 一旦 Input 改变,表单状态就会更新。这不会导致Content 重新渲染吗?结果所有Input 重新渲染??
  • 只有内容会重新渲染,因为它的状态已经改变,但其他输入不会重新渲染,因为它们的道具没有改变。
  • 是的,输入应该是纯组件。当它的道具没有改变时,需要跳过重新渲染。一般来说 Input 自然是纯组件。
  • 我已经更新了答案,因为您需要记住句柄变化。如果 Input 不是 Purecomponent,你可以在它的 export 上用 React.memo(Input) 包装它
  • 主要问题是handlechange,因为它是在我们每次重新渲染内容时创建的。
【解决方案2】:

正如天宇所说,你可以试试容器;您正在传递一个新的引用作为更改处理程序,这不仅会导致组件重新创建 jsx,还会导致虚拟 DOM 比较失败,React 将重新呈现所有输入。

您可以为 Input 创建一个纯组件容器:

const InputContainer = React.memo(function InputContainer({
  id,
  elementType,
  elementConfig,
  value,
  invalid,
  shouldValidate,
  touched,
  label,
  changed,
}) {
  //create handler only on mount or when changed or id changes
  const handleChange = React.useCallback(
    (event) => changed(event, id),
    [changed, id]
  );
  return (
    <Input
      elementType={elementType}
      elementConfig={elementConfig}
      value={value}
      invalid={invalid}
      shouldValidate={shouldValidate}
      touched={touched}
      label={label}
      handleChange={handleChange}
    />
  );
});

渲染您的 InputContainer 组件:

{row.map((formElement) => (
  <div className="col-md-3" key={formElement.id}>
    <InputContainer
      key={formElement.id}
      elementType={formElement.config.elementType}
      elementConfig={formElement.config.elementConfig}
      value={formElement.config.value}
      invalid={!formElement.config.valid}
      shouldValidate={formElement.config.validation}
      touched={formElement.config.touched}
      label={formElement.config.label}
      //re rendering depends on the parent if it re creates
      //  changed or not
      changed={props.changed}
    />
  </div>
))}

【讨论】:

  • 是的.. 我弄错了
【解决方案3】:

您必须按照一些步骤停止重新渲染。为此,我们必须使用useMemo() 钩子。

First Inside Input.jsx memoize 这个组件,如下所示。

export default React.memo(Input);

然后在Content.jsx里面,记住elementConfigshouldValidatehandleChange道具的值。因为这些道具的值是对象类型(非原始/引用类型)。这就是为什么每次传递这些 props 时,即使它们的值相同(内存位置不同),它们也不等于之前传递给该 props 的值。

const elementConfig = useMemo(() => formElement.config.elementConfig, [formElement]);
const shouldValidate = useMemo(() => formElement.config.validation, [formElement]);
const handleChange = useCallback((event) => props.changed(event, formElement.id), [formElement]);

return <..>
  <Input
    elementConfig={elementConfig }
    shouldValidate={elementConfig}
    handleChange={handleChange}
  />
<../>

据我所知,这应该可行。让我知道它是否有帮助。谢谢兄弟。

【讨论】:

  • 谢谢,@gmoniava。对不起那个错误。编辑了我的答案。
  • elementConfig 将如何获得formElement 对象??
  • @programoholic 啊……这是个问题。在循环之外,它不会得到这个变量。然后你可以尝试将整个循环包装在 useMemo 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-19
  • 1970-01-01
  • 2019-04-18
  • 1970-01-01
  • 1970-01-01
  • 2016-11-23
相关资源
最近更新 更多