使用动态创建的React组件并填充状态值

时间:2019-09-12 18:14:50

标签: javascript reactjs performance

以下是概念证明笔。我试图显示很多输入字段,并尝试在一个大对象中更改它们时收集它们的输入。如您所见,输入将不会更改其值,这正是我所期望的,因为它们是使用useEffect()创建一次并将其填充到该实例中的。

我认为解决此问题的唯一方法是在React.cloneElement更改时使用 values 并将新值注入到克隆的元素中。这就是为什么我在这支笔中创建了2000个元素的原因,这将是主要的性能消耗,因为状态发生变化时,每个元素都会重新渲染。我试图使用React.memo仅使更改后的值的输入重新呈现,但我认为cloneElement只是反过来重新呈现它,听起来好像应该将其克隆。

如何在此设置中为单个字段实现性能更新?

https://codepen.io/10uur/pen/LYPrZdg

编辑:一支笔,它具有我之前提到的cloneElement解决方案,明显的性能问题以及所有输入都被重新呈现。

https://codepen.io/10uur/pen/OJLEJqM

1 个答案:

答案 0 :(得分:1)

这是实现所需行为的一种方法: https://codesandbox.io/s/elastic-glade-73ivx

一些提示:

  1. 我不建议将React元素置于状态,而是建议将纯数据(数组,对象,...)置于将在return / render方法中映射到React元素的状态。
  2. 渲染元素数组时不要忘记使用key道具
  3. 使用React.memo可以避免在道具相同时重新渲染组件
  4. 使用React.useCallback来记住回调(在孩子上使用React.memo时会有所帮助)
  5. 使用状态设置器的功能形式访问旧状态并更新它(这在使用React.useCallback时有所帮助,并且避免在状态更改时重新创建回调)

这是完整的代码:

import React, { useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const INPUTS_COUNT = 2000;

const getInitialState = () => {
  const state = [];
  for (var i = 0; i < INPUTS_COUNT; i++) {
    // Only put plain data in the state
    state.push({
      value: Math.random(),
      id: "valueContainer" + i
    });
  }

  return state;
};

const Root = () => {
  const [state, setState] = React.useState([]);

  useEffect(() => {
    setState(getInitialState());
  }, []);

  // Use React.useCallback to memoize the onChangeValue callback, notice the empty array as second parameter
  const onChangeValue = React.useCallback((id, value) => {
    // Use the functional form of the state setter, to update the old state
    // if we don't use the functional form, we will be forced to put [state] in the second parameter of React.useCallback
    // in that case React.useCallback will not be very useful, because it will recreate the callback whenever the state changes
    setState(state => {
      return state.map(item => {
        if (item.id === id) {
          return { ...item, value };
        }
        return item;
      });
    });
  }, []);

  return (
    <>
      {state.map(({ id, value }) => {
        // Use a key for performance boost
        return (
          <ValueContainer
            id={id}
            key={id}
            onChangeValue={onChangeValue}
            value={value}
          />
        );
      })}
    </>
  );
};

// Use React.memo to avoid re-rendering the component when the props are the same
const ValueContainer = React.memo(({ id, onChangeValue, value }) => {
  const onChange = e => {
    onChangeValue(id, e.target.value);
  };

  return (
    <>
      <br />
      Rerendered: {Math.random()}
      <br />
      <input type="text" value={value} onChange={onChange} />
      <br />
    </>
  );
});

ReactDOM.render(<Root />, document.getElementById("root"));