Search code examples
javascriptreactjslistjsxreact-state-management

How to manage props of a list of components in React?


I am trying to create an ObjectList component, which would contain a list of Children.

const MyList = ({childObjects}) => {
    [objects, setObjects] = useState(childObjects)

    ...

    return (
        <div>
            {childObjects.map((obj, idx) => (
                <ListChild
                    obj={obj}
                    key={idx}
                    collapsed={false}
                />
            ))}
        </div>
    )
}

export default MyList

Each Child has a collapsed property, which toggles its visibility. I am trying to have a Collapse All button on a parent level which will toggle the collapsed property of all of its children. However, it must only change their prop once, without binding them all to the same state. I was thinking of having a list of refs, one for each child and to enumerate over it, but not sure if it is a sound idea from design perspective.

How can I reference a dynamic list of child components and manage their state?

Alternatively, is there a better approach to my problem?


Solution

  • I am new to react, probably there is a better way, but the code below does what you explained, I used only 1 state to control all the objects and another state to control if all are collapsed.

    Index.jsx

    import MyList from "./MyList";
    
    function Index() {
      const objList = [
        { data: "Obj 1", id: 1, collapsed: false },
        { data: "Obj 2", id: 2, collapsed: false },
        { data: "Obj 3", id: 3, collapsed: false },
        { data: "Obj 4", id: 4, collapsed: false },
        { data: "Obj 5", id: 5, collapsed: false },
        { data: "Obj 6", id: 6, collapsed: false },
      ];
    
      return <MyList childObjects={objList}></MyList>;
    }
    
    export default Index;
    

    MyList.jsx

    import { useState } from "react";
    import ListChild from "./ListChild";
    
    const MyList = ({ childObjects }) => {
      const [objects, setObjects] = useState(childObjects);
      const [allCollapsed, setallCollapsed] = useState(false);
    
      const handleCollapseAll = () => {
        allCollapsed = !allCollapsed;
    
        for (const obj of objects) {
          obj.collapsed = allCollapsed;
        }
        setallCollapsed(allCollapsed);
        setObjects([...objects]);
      };
    
      return (
        <div>
          <button onClick={handleCollapseAll}>Collapse All</button>
    
          <br />
          <br />
    
          {objects.map((obj) => {
            return (
              <ListChild
                obj={obj.data}
                id={obj.id}
                key={obj.id}
                collapsed={obj.collapsed}
                state={objects}
                setState={setObjects}
              />
            );
          })}
        </div>
      );
    };
    
    export default MyList;
    

    ListChild.jsx

    function ListChild(props) {
      const { obj, id, collapsed, state, setState } = props;
    
      const handleCollapse = (id) => {
        console.log("ID", id);
    
        for (const obj of state) {
          if (obj.id == id) {
            obj.collapsed = !obj.collapsed;
          }
        }
        setState([...state]);
      };
    
      return (
        <div>
          {obj} {collapsed ? "COLLAPSED!" : ""}
          <button
            onClick={() => {
              handleCollapse(id);
            }}
          >
            Collapse This
          </button>
        </div>
      );
    }
    
    export default ListChild;