score:20

Accepted answer

The issue is related to:

function handleChange(obj) {
  return (event) => setObjects(objs.map((o) => {
    if (o === obj) return {...obj, value: event.target.value}
    return o;
  }));
}

In this, you will update the objs array. This is obviously fine, but React doesn't know what has changed, so triggered Render on all the Children.

If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost.

https://reactjs.org/docs/react-api.html#reactmemo

const MyInput = React.memo(({obj, onChange}) => {
    console.log(`Rerendered: ${obj.label}`);
    return <div style={{display: 'flex'}}>
        <label>{obj.label}</label>
        <input type="text" value={obj.value} onChange={onChange} />
    </div>;
}, (prevProps, nextProps) => prevProps.obj.label === nextProps.obj.label && prevProps.obj.value === nextProps.obj.value);

However, React.Memo only does a shallow comparison when trying to figure out if it should render, so we can pass a custom comparison function as the second argument.

(prevProps, nextProps) => prevProps.obj.label === nextProps.obj.label && prevProps.obj.value === nextProps.obj.value);

Basically saying, if the label and the value, on the obj prop, are the same as the previous attributes on the previous obj prop, don't rerender.

Finally, setObjects much like setState, is also asynchronous, and will not immediately reflect and update. So to avoid the risk of objs being incorrect, and using the older values, you can change this to a callback like so:

function handleChange(obj) {
  return (event) => {
    const value = event.target.value;
    setObjects(prevObjs => (prevObjs.map((o) => {
      if (o === obj) return {...obj, value }
      return o;
    })))
  };
}

https://codepen.io/anon/pen/QPBLwy?editors=0011 has all this, as well as console.logs, showing if something rerendered.


Is there a more efficient way to approach this?

You are storing all your values in an array, which means you don't know which element needs to be updated without iterating through the whole array, comparing if the object matches.

If you started with an Object:

const startingObjects = 
  new Array(100).fill(null).reduce((objects, _, index) => ({...objects, [index]: {value: 'value', label: index}}), {})

After some modifications, your handle function would change to

function handleChange(obj, index) {
  return (event) => {
    const value = event.target.value;
    setObjects(prevObjs => ({...prevObjs, [index]: {...obj, value}}));
  }
}

https://codepen.io/anon/pen/LvBPEB?editors=0011 as an example of this.


Related Query

More Query from same tag