React - 如何在输入更改时防止重新呈现所有输入字段

时间:2021-05-22 19:24:01

标签: javascript reactjs optimization redux

我正在实现一个使用 Json 生成的表单。从 API 中检索 Json,然后在我呈现输入元素的项目上循环。这是示例 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. 我在 React.memo 中使用了 Input.js 作为: 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]
});

3 个答案:

答案 0 :(得分: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。它将使我们能够防止其他 <Input /> 不必要的渲染。 通过这样做 forElementRows 更改不会导致其他组件的任何重新渲染。

答案 1 :(得分:1)

正如天宇所说,您可以尝试使用容器;您正在传递一个新的引用作为更改处理程序,这不仅会导致组件重新创建 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>
))}

答案 2 :(得分:0)

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

First Inside Input.jsx 像下面这样记住这个组件。

export default React.memo(Input);

然后在Content.jsx里面,记住elementConfigshouldValidatehandleChange<的值/strong> 道具。因为这些 props 的值是对象类型(非原始/引用类型)。这就是为什么每次传递这些 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}
  />
<../>

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