skip to Main Content

Im using react with typescript. I am a little confused as to how can i pass ‘global’ parent state to list of its childrens?
For example there is ‘Parent’ and list of ‘Child’ components, i want to change parent state and set it to its child components:

interface ParentProps {
    childs: ChildProps[]
}

interface ChildProps {
    isOpen: boolean
}


const ParentComponent: FC<ParentProps> = (parentProps: ParentProps) => {

    const [globalState, setGlobalState] = useState(false);

    const handleClick = () => {
        setOpenState(!changeOpenChildsState);
        // So here i should something like iterate over all childrens and change its state but how ?
    }

    return (
        <>
        <button onClick= { handleClick } />

        parentProps.childs.map(child => {
            <Child isOpen={ child.isOpen } />
        })
        </>
    )
}

const Child: FC<ChildProps> = (childProps: ChildProps) => {

    const [openState, setOpenState] = useState(childProps.isOpen);

    const handleChildClick = () => {
        setOpenState(!openState);
    }

    return (
        <div>
             <button onClick= { handleChildClick } />
             <div> is child open ? { openState } </div>
        </div>
    )
}

2

Answers


  1. Without using useContext and useReducer to create state management in your application, maybe something like this could work where you populate a reference on your parent with the functions you need to call. When each of the child components mounts, it passes its setter function to its parent:

    type ChildFns =  React.MutableRefObject<any[] | undefined>;
    
    interface ParentProps {
      childs: ChildProps[]
    }
    
    interface ChildProps {
      isOpen: boolean
      childFn: ChildFns,
    }
    
    function ParentComponent(parentProps: ParentProps) {
    
      const [globalState, setGlobalState] = useState(false);
      const childFns = useRef([]);
    
      const handleClick = () => {
        // set all child booleans to false
        childFns.current.forEach((childFn) => childFn(false));
      }
    
      return (
          <>
          <button onClick= { handleClick } />
    
          { parentProps.childs.map((child, i) => {
              // eslint-disable-next-line react/no-array-index-key
              return <Child isOpen={ child.isOpen } childFn={childFns}/>
          }) }
          </>
      )
    }
    
    function Child(childProps: ChildProps) {
    
      const [openState, setOpenState] = useState(childProps.isOpen);
    
      const handleChildClick = () => {
          setOpenState(!openState);
      }
    
      useEffect(() => {
        childProps.childFn.current?.push(setOpenState);
      }, [])
    
      return (
          <div>
               <button onClick= { handleChildClick } />
               <div> is child open ? { openState } </div>
          </div>
      )
    }
    
    Login or Signup to reply.
  2. If what you want is to have a "toggle all" button, you can just use useEffect and toggle all of the children to the global value when the global state is passed as a prop:

    const Parent = () => {
      const [globalState, setGlobalState] = React.useState(false);
      const handleClick = () => {
        setGlobalState(prev => !prev);
      }
      
      return (
        <main>
          <button className="set-all" onClick={handleClick}>Set All</button>
          {[1,2,3,4,5].map((_,i) => (
            <Child key={i} isOpen={globalState} />
          ))}
        </main>
      )
    }
    
    const Child = ({ isOpen }) => {
      const [openState, setOpenState] = React.useState(isOpen);
      const handleClick = () => {
        setOpenState(prev => !prev);
      }
      
      React.useEffect(()=>{
        setOpenState(isOpen)
      }, [isOpen])
    
      return (
        <React.Fragment>
          <button onClick={ handleClick }>Toggle State</button>
          <p>Is child open ? { openState ? 'yes' : 'no'}</p>
        </React.Fragment>
      )
    }
    
    const root = ReactDOM.createRoot(document.getElementById("root"))
    root.render(<Parent/>)
    main {
      display: grid;
      grid-template-columns: max-content auto;
      gap: .5rem;
      align-items: center;
    }
    
    .set-all {
      grid-column: 1 / -1;
      width: max-content;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
    <div id="root"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search