skip to Main Content

Problem Summary:

I have a parent component that has a state. I need this state changed from component d.

The problem is this the child of a child of a child component, as pictured below:

Component Tree

I just need an example of the state changing from true to false and vice versa.

What I tried:

  • Tried passing props, but props are immutable

  • Tried creating a function that does this and passing that function as a prop, but when I call it from component D it says I can’t pass an argument.

  • My closest try was passing the function that sets the state from parent to child, but it never actually sets anything:

import './App.css';

export default function App() {
    
  const [isLame, setIsLame] = useState(false);
        
  return (
    <div className="React">
        <h1>React is lame</h1>
          <ChildComponentB stateChanger={setIsLame} />
    </div>
  );
}

I have another part of the application where this works, but because I have so many nested children, I can’t pass them through all the children. For example:

Child Component B contains child component C:

        
... 
  return (
    <div className="React">
        <h1>React is lame</h1>
          <childComponentC stateChanger={setIsLame} />
    </div>
  );
}

… and Child Component C contains child component D:

export default function childComponentC() {
        
... 
  return (
    <div className="React">
        <h1>React is lame</h1>
          <childComponentD stateChanger={setIsLame} />
    </div>
  );
}

…And child component D contains the condition where I need the change the state in the parent component:

export default function childComponentD() {
        
... 

   //Tell the parent react is lame
   setIsLame(true);

  return (
    <div className="React">
        <h1>React is lame</h1>
    </div>
  );
}

…Does anyone have an example of something like this working?

Follow up stuff based on comments / responses:

  • I can’t use some external tool like redux

  • The error I’m getting is that setIsLame(true) is not a function.

2

Answers


  1. you can use some state management like TanStack Query this is a powerful asynchronous state management for react or use redux this tow tools are the most famous tools used with react

    you can find some vid on youtube explaning that tools

    Login or Signup to reply.
  2. The error "setIsLame is not a function" occurs because the initial setIsLame function in the root App component is passed down as stateChanger for some reason. Then each component from there appears to pass along an undefined setIsLame variable/reference to yet another stateChanger prop. I don’t see any of the components consuming any React props either, so even if they passed stateChanger={stateChanger} as a prop it’d still be undefined.

    Each component must actually consume the props they desire to pass down.

    Example:

    export default function App() {
      const [isLame, setIsLame] = useState(false);
            
      return (
        <div className="React">
          <h1>React is lame</h1>
          <ChildComponentB setIsLame={setIsLame} /> // <-- setIsLame prop passed
        </div>
      );
    }
    
    export default function ChildComponentB({ setIsLame }) { // <-- setIsLame prop consumed
      ... 
    
      return (
        <div className="React">
          <h1>React is lame</h1>
          <ChildComponentC setIsLame={setIsLame} /> // <-- setIsLame prop passed
        </div>
      );
    }
    
    export default function ChildComponentC({ setIsLame }) { // <-- setIsLame prop consumed
      ... 
    
      return (
        <div className="React">
          <h1>React is lame</h1>
          <ChildComponentD setIsLame={setIsLame} /> // <-- setIsLame prop passed
        </div>
      );
    }
    
    export default function ChildComponentD({ setIsLame }) { // <-- setIsLame prop consumed
      ... 
    
      return (
        <div className="React">
          <h1>React is lame</h1>
          <button type="button" onClick={setIsLame}> // <-- setIsLame prop used
            Announce that React is lame
          </button>
        </div>
      );
    }
    

    This resolves the issue of passing the correct prop value down through all that apps… but this is a complete anti-pattern as now all components between App and ChildComponentD necessarily need to be aware of this setIsLame prop and pass it down even though they don’t care about or us it.

    This problem known as "Props Drilling".

    To address this problem you should introduce a React Context to the app so that App can provide the setIsLame function to its sub-ReactTree and only the descendent components that care about it explicitly access the provided context value.

    Example Implementation:

    import { createContext, useContext } from 'react';
    
    export const ReactIsLameContext = createContext({
      setIsLame: () => {}
    });
    
    export const useReactIsLame = () => useContext(ReactIsLameContext);
    
    const ReactIsLameProvider = ({ children }) => {
      const [isLame, setIsLame] = useState(false);
    
      return (
        <ReactIsLameContext value={{ setIsLame }}>
          {children}
        </ReactIsLameContext>
      );
    };
    
    export default ReactIsLameProvider;
    
    export default function App() {
      return (
        <div className="React">
          <h1>React is lame</h1>
          <ReactIsLameProvider>
            <ChildComponentB /> // <-- no props passed
          </ReactIsLameProvider>
        </div>
      );
    }
    
    export default function ChildComponentB() { // <-- no props consumed
      ... 
    
      return (
        <div className="React">
          <h1>React is lame</h1>
          <ChildComponentC /> // <-- no props passed
        </div>
      );
    }
    
    export default function ChildComponentC() { // <-- no props consumed
      ... 
    
      return (
        <div className="React">
          <h1>React is lame</h1>
          <ChildComponentD /> // <-- no props passed
        </div>
      );
    }
    
    export default function ChildComponentD() { // <-- no props consumed
      const { setIsLame } = useReactIsLame(); // <-- access setIsLame from context
    
      ... 
    
      return (
        <div className="React">
          <h1>React is lame</h1>
          <button type="button" onClick={setIsLame}>
            Announce that React is lame
          </button>
        </div>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search