Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Huge React State Array with Hundreds of Inputs, slow state changes onChange

I'm trying to a large React form, but each input in the form is complex and has a complex state. I keep the state of all of the inputs in a parent component, and each input is wrapped in a child component. I have a state array in parent that contains the current value of the state for all of the input elements. Currently, every time there's an onChange in the input, I try to reset the entire state array in the parent via setState(). This was okay with up to 5 inputs, but once I got above that (like a hundred inputs), I started to notice some serious lag in the program. Please note: the program will also allow you to rearrange, delete, and add inputs, so the state needs to accommodate those changes Ie. the first input could swap positions with the 2nd input or be inserted right after the 10th input.

My objective is to find a way to optimize the performance of this onChange. Ultimately, I don't really need the data to be in the parent component, I just need to collect the values for the inputs when I click save at the bottom of the page.

Just to reiterate, I have two components.

  1. A Parent Component

  2. A Child Component

The Child component is basically an input where users can write hundreds of lines of text.

The Parent component contains 100s of the children and essentially looks like this:

export default function Parent(props) {
  const [state, setState] = useState(createDummyData());
  useEffect(() => {});



  const onInputChange = (value, index) => {
    var tempValue = [...state];
    tempValue[index] = value;
    setState(tempValue);
  };

  return (
    <>
      <div style={{ display: "flex", flexDirection: "column" }}>
        {state.map((item, index) => (
          <Child
            value={state[index].content}
            index={index}
            onChange={onInputChange}
          />
        ))}
        <button style={{backgroundColor: "red"}}>save input data</button>
      </div>
    </>
  );
}

The child component looks like this

export default function Child(props) {
  useEffect(() => {});

  const onChange = event => {
    props.onChange(event.target.value, props.index);
  };

  return (
    <>
      <input value={props.value} onChange={onChange} />
    </>
  );
}

I haven't found a simple way around this. Some seem to suggest using Redux, others seem to say use a combination of useMemo and useEffect to prevent rerenders. Your help would be much appreciated.

One thing I noticed is that if I try to keep individual state within the Child components, they render onChange much faster. This is probably because it doesn't have to setState for the parent state array each time. If possible, I'd like to be able to simply go through and grab the state of the child nodes when I click save. Would I use a ref in this case? Is it possible to do it without a ref?

I'd also like to AVOID using onBlur() just for the purpose of this project

The codesandbox is copied down below for reference: https://codesandbox.io/s/cocky-knuth-jm8n6?fontsize=14

like image 895
andul007 Avatar asked Oct 30 '19 07:10

andul007


People also ask

Why React state is not updated immediately?

State updates in React are asynchronous; when an update is requested, there is no guarantee that the updates will be made immediately. The updater functions enqueue changes to the component state, but React may delay the changes, updating several components in a single pass.

What happens every time when there is a change in the state data in React?

When a value in the state object changes, the component will re-render, meaning that the output will change according to the new value(s).

How does React handle multiple state updates?

React may batch multiple setState calls into a single update for performance. Because props and state may be updated asynchronously, you should not rely on their values for calculating the next state.

How do I listen for state changes in react?

Use the useEffect hook to listen for state changes in React. You can add the state variables you want to track to the hook's dependencies array and the logic in your useEffect hook will run every time the state variables change. Copied! The second parameter we passed to the useEffect hook is an array of dependencies.

How to manage react state with arrays?

You have learned about different ways on how to manage React state with arrays. You can add items to an array, update an item in the array or update the whole array, and remove items from an array. Everything is possible with only JavaScript without using React for it. React is only used to set the state in the end.

Why is it so hard to update a nested state in react?

The pain of updating a nested state stems from the fundamental architectural decision of the React to embrace immutability. Immutability comes with plenty of great benefits, such as predictability and performance, so the trade-off is worth it. There are two main ways to deal with the problem of updating a deeply nested state.

How does state work in react?

No matter the kind of technology you use, your app relies on some sort of state, be it in-memory, on a physical disc, and so on. In React, every component can handle its own state, which might mean clicking a button or the content of a text input.


2 Answers

Created example with similar functional components and dummy data:

https://codesandbox.io/s/recursing-cdn-q4vx0

Have to create Child component with local item state and sync with main array. Added Delete and Add.

Work with object arrays as your sample.

like image 77
Oleg Avatar answered Sep 22 '22 23:09

Oleg


Copying large arrays shouldn't be the culprit, it only copies object references which is usually very fast, not an actual deep copy. In fact, Redux itself does it this way.

The reason you're experiencing this lag is you didn't set keys to the children. You should set unique keys and don't use the index as the key.

like image 34
Mordechai Avatar answered Sep 21 '22 23:09

Mordechai