This question is more for understanding more how react handles and reacts to changes, than implementation, therefore I’m letting immutable-props-apprach go for a little bit.
I’m trying to get the first element of an array and remove it from the original array, which was passed to component as a prop:
const ChildComponent = (
arrayToChange: Array<any>[]
) => {
const elementFromArray = arrayToChange.shift();
}
From the definition of the shift() method::
The shift() method removes the first element from an array and returns that removed element. This method changes the length of the array.
Even though the elementFromArray variable now contains the element from array, the array is intact, it was not affected in any way and still contains all the elements.
But how is that possible? React should pass the props by reference, therefore the original array should be affected. I’d understand if React had some protective measures in place and the changes wouldn’t be reflected in the parent, however, I’d still expect changes being reflected in the child.
I cannot find anything useful which would explaing this behaviour, majority of the resources only mention the immutable approach to props and how to find a way around it, not the reasons or logic behind it.
Even though the elementFromArray variable now contains the element from array, the array is intact, it was not affected in any way and still contains all the elements. However, if I use push() method, then the changes are reflected, arrayToChange contains one more element.
My question then is – why does the arrayToChange react differently to these methods? If shift() doesn’t change the contents, I’d expect push() wouldn’t either.
2
Answers
I am not entirely sure what this question is but I am going on a limb here to take a guess at it so please comment here and I will change before downvoting.
I think you should try this, in your child component:
Then use "data" to map through to output in your jsx
Then in your parent component, do the shift on the arrayToChange. You can think of the useEffect as a "watcher" which will fire when the length of the array changes.
The behaviour in the snippet can be explained if you see the rendering process as a breadth-first algorithm.
JSX will convert:
Into the following JavaScript:
React.createElement(ChildShowArray, { array: letters })
creates a structure that does not immediately invoke theChildShowArray
component. It will create some sort of intermediate structure/object that only runs whenever the renderer asks it to run.JavaScript placed within
{...}
(JSX context) are passed directly as arguments, and therefore are resolved directly. Meaning that all{JSON.stringify(letters)}
insideParent
runs before any of the code in child components runs.When building the parent structure is complete, the renderer will visit each intermediate structure/object and asks it to render. This is done from top to bottom, which is why the first
ChildShowArray
render does still show the full array. ThenChildChangeArray
is rendered which removes the first element. The secondChildShowArray
render reflects this change, and is rendered without the first element.Note that
shift()
does change the contents ofletters
, but when it’s called the content ofParent
is already rendered and no longer changes. This change does impactParent
on the next render (click "re-render" button in snippet). It also impacts renders of other child components below it that use the same array reference.