'React performance: developing recursive component JSON

I have developed a react component

  1. where JSON is array of object and each object could have another array which is same as parent array as prop. It can go till any depth.
  2. I have used a recursive component to render each component and by calling the same function recursive if the object has anychild/childs
  3. One of the specific requirement is whenever a node has to be checked/unchecked its Parent need to notified and Parent will change if all of its child are checked.

JSON data

[ 
    {
       name: "name",
       checked: false
       children: [{
         name: "name",
         checked: false
        }]    
    },
    {
       name: "name"
       checked: false,
       children: [{
         name: "name",
         checked: false,
         children: [{
             name: "name",
             checked: false
         },{
             name: "name",
             checked: false
         }]    
      },
      {
         name: "name",
         checked: false
      }]    
    },
    .....
]

App.jsx

const root = (JSONdata) => {
    const [data, setData] = useState(JSONdata)
    const updateData = (nodeToUpdate, positionOfNode) {
        //newData is different referance return by updateNodeAtSpeifiedDeepPosition function as we don't want to mutate data  set in state
        const newData = updateNodeAtSpeifiedDeepPosition(data, nodeToUpdate, positionOfNode);
        setData[newData]
    }
    return ( 
         <Tree data={JSONdata} updateData={updateData} />
    )
}

Tree.jsx

const Tree = (data) => {
    const [treeNode, setTreeNode] = useEffect(data)
    const renderTree = (nodes) => {
        <li onClick = { updateData) >
            If(treeNode.child) {
                <Tree data = {treeNode.child} />

            } </li>
    }

    return ( <ol >renderTree(treeNode) </ol>
    )
}

Its working but on each check or uncheck root is updated and all child are again re rendering as they have new reference.

Is there way to performance optimise it ?



Solution 1:[1]

Your implementation looks fine

On every checkbox change yes of course it will change your root data and it will rerender all the child components

But you can control re-renders of child components using React.memo

Using memo will cause React to skip rendering a component if its props have not changed.

This can improve performance. Check this for the detailed explanation

https://reactjs.org/docs/react-api.html#reactmemo

https://www.w3schools.com/react/react_memo.asp

Please note: react useMemo and react memo are not same;

Solution 2:[2]

I am not familiar with your state management, so I don't know if my suggestion is applicable.

but if you hold only ids in the tree structure, and alongside it, a map id -> Node. Plus the parent will pass only the id to the children and each child will get its data (name, checked) from somewhere else (there are multiple options here) then the parent is rendered once.

React.memo is easier, though the memorization has its cost

Solution 3:[3]

Apparently whenever we do update in parent and update it checked status child will be rerender.

Solution 4:[4]

Alongside, using the key prop on li elements. You can also wrap your Tree component within React.memo(), this creates a higher order component, which prevents unnecessary re-renders, if the props passed to it have not changed.

After wrapping it within React.memo(), your tree component should look like this:

    const Tree = React.memo((data) => {
    const [treeNode, setTreeNode] = useEffect(data)
    const renderTree = (nodes) => {
        <li onClick = { updateData) >
            If(treeNode.child) {
                <Tree data = {treeNode.child} />

            } </li>
    }

    return ( <ol >renderTree(treeNode) </ol>
    )
});

This blog post might help more, in understanding the concept.

To fulfill your third requirement, I need to know, what will be the value of positionOfNode, within updateData function.

 const updateData = (nodeToUpdate, positionOfNode) {
    const newData = updateNodeAtSpeifiedDeepPosition(data, nodeToUpdate, positionOfNode);
    setData[newData]
}

My suggestion is since your data is an array of JSON objects, let's use array indices to our advantage, provided that the order of elements doesn't change within the array. Assuming that, the position of a node can be defined as a concatenated string of indices. Consider the following data:

[ 
{
   name: "name1",
   checked: false
   children: [{
     name: "name2",
     checked: false
    }]    
},
{
   name: "name3"
   checked: false,
   children: [{
     name: "name4",
     checked: false,
     children: [{
         name: "name5",
         checked: false
     },{
         name: "name6",
         checked: false
     }]    
  }];

For the node named name1, its position should be 0. For the node name name2, its position should be its parent position + its index in the children array, hence 00. Similarly, the position of name3 will be 1. Similarly, the position of name4 will be 10. Similarly, the position of name5 will be 100. Similarly, the position of name6 will be 101.

Using these positions in the updateData function, the updateNodeAtSpeifiedDeepPosition can be written to notify the parent of a node as well, since its index is already present in the position string.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 sojin
Solution 2
Solution 3 sachin kumar
Solution 4