reactjswordpress

setAttributes not iterating in useEffect


i'm trying to implement my block element for wordpress with jsx, everything looks fine except this loop that didn't work. I try to update my attributes menuElem (an array with default: []) with a list coming from my fetch. But i keep having, outside the useEffect only one element stored in my attribute. Here's the code, i miss something but i'm lost right now :(

useEffect(
    function () {
      apiFetch({ path: "/wp/v2/menu-items/" }).then((items) => {
        items.map((item, index) => {
          if (item.menus === props.attributes.menuID) {
            props.setAttributes({
              menuElem: [
                {
                  id: index,
                  url: item.url,
                  title: item.title.rendered,
                },
              ],
            });
          }
        });
      });
    },
    [props.attributes.menuID]
  );

And i want to loop in my jsx right after

return (
    <div {...blockProps}>      
      <nav className="menu menu__main">
        <ul>
          {props.attributes.menuElem.map((item) => (
            <li key={item.id}>{item.title}</li>
          ))}
        </ul>
      </nav>
    </div>
  );

I tried with something else, like working with an array where i push the results of my fetch, but the same problem keeps happening, the loop in useEffect is doing its job, but once i'm out of it, i keep having only one item instead of the whole result


Solution

  • The issue here is how you're using the map function. map returns an array after looping through all the items which I suspect is what you need menuElem to be.

    The current code would set the menuElem repeatedly based on the condition, therefore overwriting the initial. At the end of the iteration, menuElem is left with the last item that meets the condition.

    Here is a revised version of the code using forEach method instead

    useEffect(() => {
      apiFetch({ path: "/wp/v2/menu-items/" }).then((items) => {
        const updatedMenuElem = [];
    
        items.forEach((item, index) => {
          if (item.menus === props.attributes.menuID) {
            updatedMenuElem.push({
              id: index,
              url: item.url,
              title: item.title.rendered,
            });
          }
        });
    
        // Update the attribute with the updatedMenuElem array
        props.setAttributes({ menuElem: updatedMenuElem });
      });
    }, [props.attributes.menuID]);