Let's say I declare some state which represents a graph, modeled as an object which looks like this:
const [graph, setGraph] = useState({
nodes: [{
id: 1, name: "abc",
},
{
id: 2, name: "def",
},
{
id: 3, name: "ghi",
},
],
edges: [[1,2], [2,3]]
})
I want to be able to watch changes like:
- adding a new node
- modifying an existing node
- adding a new edge
- removing an edge
and be able to watch and react to those changes.
I could do something like this, for example to add a new node:
setGraph((graph) => ({
...graph,
nodes: [
...graph.nodes,
newNode
]
})
However, imagine having deeper nestedness instead of just one level - this becomes impractical fairly quickly. With updating a node, this would be even more cumbersome to do in a functional style, having to filter
for nodes with an id
different than the one edited and adding the edited version back into the array.
Is there a simpler way to watch the whole nested structure and react to changes to it or to any of its members (and their members, and so on)?
For example, let's say I want to call a function drawGraph
that re-draws the entire graph any time it changes (this is due to other business constraints). How do I make sure to call that function each and every time a node or edge is added or updated?
CodePudding user response:
Try immer.js, with it you just specify what has changed at any level of nesting and it will automatically take care of all the changes that need to be handled. The answer ofcourse is to a more general question of how do i manually update deeply nested object properties without having to manually duplicate all the objects at each level. https://immerjs.github.io/immer/
CodePudding user response:
you can create a deep copy and store it in a new variable then use it in setState. Spread operator doesn't deep copy nested objects. you can do it in few other ways.
const tempGraph = JSON.stringify({...graph, nodes: [...graph.nodes, newNode]})
setGraph(JSON.parse(tempGraph))
you can also install lodash and deep copy using it's cloneDeep function.
now any edge or node update no matter how deeply nested it is should re-render your component. and you can put the graph state in your useEffect dependency array to run your function
useEffect(()=>{
somFunction();
},[graph])
CodePudding user response:
You can use JSON.stringify(graph)
instead of graph
as dependency of the array
useEffect(()=>{
whatever();
},
[JSON.stringify(graph)]); // instead of [graph]);