I have a parent component ParentItem
which renders ChildItem
components.
ParentItem
passes removeItem()
to ChildItem
components during render.
Normally, anytime useState
is called and the new state is different from the previous state, the component should re-render.
For some reason, in this case it seems like removeItem()
is passed to the children with childItemList
at the time of render, so if one ChildItem
calls removeItem()
the next child will have stale a childItemList
(ie they will have the initial childItemList
with all children, when I want the previously removed child to be reflected in the following calls to removeItem()
. I feel like the problem is that handleClick()
is being called inside of panResponder
which is useRef
, but i'm not sure if thats really the case, and if it is why.
I have resorted to using a useRef
copy of childItemList
, make changes in the useRef
copy and passing it to setChildItemList()
within removeItem()
.
But I don't like how I need two variables to track the list of child components. I feel like there is probably a better way to go about this.
Parent:
const ParentItem = () => {
const [childItemList, setChildItemList] = useState([
{id:"a", name: "ChildItemA"},
{id:"b", name: "ChildItemB"},
{id:"c", name: "ChildItemC"},
{id:"d", name: "ChildItemD"}
]);
const removeItem = (itemId) => {
setChildItemList([...items].filter(item => item.id !== itemId));
}
return(
<View>
{
childItemList.map((item) => {
return(
<ChildItem
key={Math.random()}
handleClick={removeItem}
details={item}
/>
)
})
}
</View>
)
}
export default ParentItem;
Child:
const ChildItem = (props) => {
const pan = useRef(new Animated.ValueXY()).current;
const panResponder = useRef({
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
Animated.timing( pan, {
toValue: { x: -10, y -10 },
duration: 1000,
useNativeDriver: false
}).start(()=>{
props.handleClick(props.details.id);
})
},
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
}
})
})
return(
<View>
<Animated.View
{...panResponder.panHandlers}
>
</Animated.View>
</View>
);
}
export default ChildItem;
Parent: Current solution using useRef copy of childItemList
const ParentItem = () => {
const [childItemList, setChildItemList] = useState([
{id:"a", name: "ChildItemA"},
{id:"b", name: "ChildItemB"},
{id:"c", name: "ChildItemC"},
{id:"d", name: "ChildItemD"}
]);
/* useRef copy of childItemListRef */
const childItemListRef = useRef([childItemList]);
const removeItem = (itemId) => {
/* Set childItemListRef, then pass it to setChildItemList */
childItemListRef.current = childItemListRef.current.filter(item => item.id !== itemId);
setChildItemList(childItemListRef);
}
return(
<View>
{
childItemList.map((item) => {
return(
<ChildItem
key={Math.random()}
handleClick={removeItem}
details={item}
/>
)
})
}
</View>
)
}
export default ParentItem;