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:
- Write a generic
update(arr, index, value)
function - Create memoized callback using
useCallback
hook - Curried function makes it easier to use an co-located parameters with event data
- 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>
}