React child component not re-rendering on updated parent state

Viewed 97

I've tried to find a solution to this, but nothing seems to be working. What I'm trying to do is create a TreeView with a checkbox. When you select an item in the checkbox it appends a list, when you uncheck it, remove it from the list. This all works, but the problem I have when I collapse and expand a TreeItem, I lose the checked state. I tried solving this by checking my selected list but whenever the useEffect function runs, the child component doesn't have the correct parent state list.

I have the following parent component. This is for a form similar to this (https://www.youtube.com/watch?v=HuJDKp-9HHc)

export const Parent = () => {
 const [data,setData] = useState({
   name: "",
   dataList : [], 
   // some other states
  })

 const handleListChange = (newObj) => {
   //newObj : { field1 :"somestring",field2:"someotherString" }
   setDataList(data => ({ 
     ...data,
      dataList: data.actionData.concat(newObj)
   }));

   return (
     {steps.current === 0 && <FirstPage //setting props} 
      ....

     {step.current == 3 && <TreeForm dataList={data.dataList} updateList={handleListChange}/> 
   )

 }


The Tree component is a Material UI TreeView but customized to include a checkbox enter image description here

Each Node is dynamically loaded from an API call due to the size of the data that is being passed back and forth. (The roots are loaded, then depending on which node you select, the child nodes are loaded at that time) .

My Tree class is

export default function Tree(props) {
 
useEffect(() => {

// call backend server to get roots 
setRoots(resp) 

})

return ( 
<TreeView >
Object.keys(root).map(key => ( 
<CustomTreeNode key={root.key} dataList={props.dataList} updateList={props.updateList}

)))}

</TreeView>

)

CustomTreeNode is defined as

export const CustomTreeNode = (props) => {

const [checked,setChecked] = useState(false)
const [childNodes,setChildNodes] = useState([])

async function handleExpand() {

  //get children of current node from backend server 
  childList = []
  for( var item in resp) {

     childList.push(<CustomTreeNode dataList={props.dataList} updateList={props.updateList} />)
   } 

  setChildNodes(childList) 
}

const handleCheckboxClick () => {
  if(!checked){
   props.updateList(obj)
   }
else{
   //remove from list 
}

  setChecked(!checked)

}

// THIS IS THE ISSUE, props.dataList is NOT the updated list. This will work fine
// if I go to the next page/previous page and return here, because then it has the correct dataList. 
useEffect(() => {

        console.log("Tree Node Updating")
        var isInList = props.dataList.find(function (el) {
            return el.field === label
        }) !== undefined;

        if (isInList) {
            setChecked(true);
        } else {
            setChecked(false)
        }
    }, [props.dataList])

return ( <TreeItem > {label} </TreeItem> )

}

1 Answers

You put props.data in the useEffect dependency array and not props.dataList so it does not update when props.dataList changes.

Edit: Your checked state is a state variable of the CustomTreeNode class. When a Tree is destroyed, that state variable is destroyed. You need to store your checked state in a higher component that is not destroyed, perhaps as a list of checked booleans.

Related