Home > Blockchain >  How to memoize child components using functions that set values to a parent array state?
How to memoize child components using functions that set values to a parent array state?

Time:12-16

I'm attempting to memoize a Child component that contains a function that sets values to a parent array state and relies on the parent state. That, however, means that if I put the parent array state as a dependency, the number of rerenders do not make a significant improvement.

const Parent = () => {
  const [state, setState] = useState([{},{},{}]);

  const setA = (index, a) => {
    let copyArr = [...state];
    copyArr[index] = a;
    setState(copyArr);
  }

  return (
   state.map((item, index)=>{
     <Child index={index} item={item} setA={setA}>
   }
  )
}


const Child = ({index, item, setA}) => {
  return (
   <View>
     <Button onClick={() => {setA(index, Math.randInt(0,100))}>
     <Text>{item.a}</Text>
   </View>
  )
}

So far what I've attempted was

const Parent = () => {
  const [state, setState] = useState([{},{},{}]);

  const setA = React.useCallBack((index, a) => {
    let copyArr = [...state];
    copyArr[index] = a;
    setState(copyArr);
  }, [state]);

  return (
   state.map((item, index)=>{
     <Child index={index} item={item} setA={setA}>
   }
  )
}


const Child = React.memo(({index, item, setA}) => {
  return (
   <View>
     <Button onClick={() => {setA(index, Math.randInt(0,100))}>
     <Text>{item.a}</Text>
   </View>
  )
});

This however falls flat because whenever state as an array is updated, all sibling components rerender, due to the function being re-created.

Alternatively if I do not use state in the dependency array, the function does not re-create and the state internally doesn't change (Closure).

I'm wondering if there is a way to properly achieve this without significant revamp of the structure.

CodePudding user response:

It is not necessary to copy the current state, instead use the previous state:

 const setA = React.useCallBack((index, a) => {
    setState(prevState => {
      const newState = [...prevState];
      newState[index] = a;

      return newState;
    })
  }, []);

Now you can remove a state from the dependency list

CodePudding user response:

  1. Write a generic update(arr, index, value) function
  2. Create memoized callback using useCallback hook
  3. Curried function makes it easier to use an co-located parameters with event data
  4. use functional update in the setState call to remove all dependencies from the callback.
// generic update array function
const update = (arr, index, value) =>
  [ ...arr.slice(0, index), value, ...arr.slice(index   1) ]

In the Parent component, we create the memoized callback and pass it to the Child -

const Parent = () => {
  const [state, setState] = useState([{},{},{}]);
  
  const setA = useCallback((index, a) => event =>
    setState(s => update(s, index, a))
  , []); // zero dependency callback

  return state.map((item, index)=>
    <Child key={index} item={item} index={index} onClick={setA}>
  )
}

Now Child will only re-render if index, item, or setA changes -

const Child = ({ item, index, onClick }) => {
  return <View>
    <Button onClick={set(index, Math.randInt(0,100))}>Click</Button>
    <Text>{item.a}</Text>
  </View>
}
  • Related