Deep Copy to Avoid Value Reference
Playing around application states in ReactJS or VueJS is quite fun. But it’s not fun anymore if we (accidentally) mutate the state while doing a search from the state.
It happens to me when I’m trying to find a sidebar ID from the store of the sidebar tree. Yes, a tree. Imagine the store if we have a sidebar tree like this
and we need to find out the ID of file2.xml
. Our best approach here is to apply a DFS (Depth-First Search) without re-render the sidebar. And here’s my first approach to DFS
- js
1 |
|
if you notice it, we’re using .shift()
and .unshift()
here. Unfortunately, the sidebar does re-render and it’s quite slow. It’s because the .shift()
time complexity is at O(n)
at worst!
Using Array.pop()
After some research, it seems that we can change from .shift()
to .pop()
. Since the .pop
behavior is different, we need to change the code accordingly
- js
1 |
|
this change is quite performant compared to use .shift()
before. But unfortunately, it still does some re-render! The sidebar items somehow got removed and added back themselves. We didn’t do a mutation to store, though. What happens?
The Culprit
Debugging for hours and finally, I found the culprit! We can see that we’re doing some assignments to a new variable called tree
here.
function dfs(id) {
const tree = [state.list] // <-- this assignment still have value reference to the original
...
In the JS world, doing assignments from an array won’t cut the value reference to the original array. In this particular case, it’s the state.list
. Here’s an example for value reference of an array
- js
1 |
|
the solution is to deep copy the array. So the nested structure will be copied without any reference to the original. To do the deep copy, we have several methods.
JSON.stringify + JSON.parse duet
Here is an example of deep copy using these duet
- js
1 |
|
By far, this is the simplest, yet native approach without using any library. But read these gotchas
here is a demonstration of it (copied from this gist)
- js
1 |
|
With these gotchas, we’ll try next method
Lodash _.cloneDeep
The lodash library is very popular among JS developers. Lodash has several utility functions that make coding in JS easier and cleaner. Using _.cloneDeep
is quite easy too. Here’s an example
- js
1 |
|
Conclusion
Deep copy is the answer for this particular case. If we need to avoid mutation to the original object, we can use this method to avoid that. And here’s the final version using deep copy
- js
1 |
|
References
- https://javascript.plainenglish.io/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089
- https://www.samanthaming.com/tidbits/35-es6-way-to-clone-an-array/
- https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript?page=1&tab=votes#tab-top
- https://dev.to/samanthaming/how-to-deep-clone-an-array-in-javascript-3cig
- https://lodash.com/