Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive function in Reactjs Hooks?

I want to update the state using react Hooks useState(); ?

Here is an example :

I have global state on top of the app:

const [familyTree, setFamilyTree] = useState([
    {
      fam_id: 1,
      name: "No name",
      attributes: {
        "Key1": "*",
        "Key2": "*",
      },
      children: [
        {
          fam_id: 2,
          name: "No Name2",
          attributes: {
            "Key1": "*",
            "Key2": "*",
          },
        },
      ],
    },
  ]);

I have a current object to update the global state:

let res = {
          fam_id: 2,
          name: "No Name2",
          attributes: {
            "Key1": "Update this",
            "Key2": "*",
          },
        },

Recursive function in this case helps me to update global state with matched ID, but I have problem now,

const matchAndUpdate = (updater, target) => {
      if (updater.fam_id === target.fam_id) {
        target.name = updater.name;
        target.attributes = updater.attributes;
      }

      if ("children" in target && Array.isArray(target.children)) {
        target.children.forEach((child) => {
          matchAndUpdate(updater, child);
        });
      }
    };
    familyTree.forEach((g) => {
      matchAndUpdate(res, g);
      setFamilyTree({ ...g }); // here is my try, this works on start, but on secound update i got error about forEach is not a function...
    });

I don't know where to update state on correct way?

Thanks, o/

like image 201
Milosh N. Avatar asked Mar 15 '26 15:03

Milosh N.


1 Answers

Because you update state inside of forEach().

Maybe you should use .map and update state then at the end of check array.

This is the solution:

const matchAndUpdate = (updater, children) => {
    return children.map(_child => {
      if (updater.fam_id === _child.fam_id) {
        return {
          ...updater,
          children: _child.children && Array.isArray(_child.children) ? matchAndUpdate(updater, _child.children) : null
        };
      } else {
        return {..._child,children: _child.children && Array.isArray(_child.children) ? matchAndUpdate(updater,_child.children) : null};
      }
    });
  };

This will return and array of children, so you will begin from the initial array:

 const finalFamily = matchAndUpdate({ fam_id: 1, name: "Name" }, familyTree);

finalFamily will be the final updated array.

You can update the state like this:

// Option 1:
setFamilyTree(matchAndUpdate({ fam_id: 1, name: "Name" }, familyTree);

// Option 2:
const newFamilyTree = matchAndUpdate({ fam_id: 1, name: "Name" }, familyTree);
setFamilyTree(newFamily);

--- NEXT QUESTION-- -

I understand that you want to create a method to push new children to child specified by id.

I developed a method that maintains attributes and old children:

  const addChildrenToChild = (parent,numChildren) => {
    const arrayChildren = [];
    for (let i = 0; i < numChildren; i++) {

      arrayChildren.push({
        fam_id: Math.floor(Math.random() * 100),
        name: "No name",
        attributes: {
        key1:"",
        key2:""
        },

      });
    }

    return {...parent,children:parent.children && Array.isArray(parent.children) ? parent.children.concat(arrayChildren) : arrayChildren }
  }

And upgrade matchAndUpdate to maintains old children

 const matchAndUpdate = (updater, children) => {
    return children.map(_child => {
      if (updater.fam_id === _child.fam_id) {
        return {
          ...updater,
          children: updater.children
          //Filter updater children 
            .filter(_childFiltered =>
              _child.children && Array.isArray(_child.children) ? 
              //check if exists new child in old children
              _child.children.some(
                _childToCheck => _childToCheck.fam_id !== _childFiltered.fam_id
              ) : true
            ) 
            //concat old children and check to update
            .concat(
              _child.children && Array.isArray(_child.children)
                ? matchAndUpdate(updater, _child.children)
                : []
            )
        };
      } else {

        return {
          ..._child,
          children: 
              _child.children && Array.isArray(_child.children)
                ? matchAndUpdate(updater, _child.children)
                : []
        };
      }
    });
  };

And now. You can use the other method at the same time to add new children:

// Now we are going to add new children to the first element in familyTree array, and maintains old children if it has.
const newFamilyTree = matchAndUpdate(
    addChildrenToChild(familyTree[0], 10),
    familyTree
  );

setFamilyTree(newFamilyTree);

like image 199
Adrian Naranjo Avatar answered Mar 17 '26 10:03

Adrian Naranjo