skip to Main Content

Consider the following React Component Tree:

<A>

  <B>
    <D>
      <E/>
    </D>
  </B>

  <C>
    <D>
      <E/>
    </D>
  </C>

</A>

A, B, C, D and E are all components. Components don’t have a closing tag, but here I mean that A renders B and C, then D is rendered BY B and C components and so on.

I have a context (which is also a state variable) in A, disabledChildCtx initialized by a string "B". The context in its entire lifetime would just toggle between the values "B" and "C". This context here, is meant to disable some buttons in either B or C at a time according to value.

The component E uses the context and looks something like:

const E = () => {
  const ctxVal = useContext(disabledChildCtx);
  return <button disabled = {/*.... some logic ....*/}></button>
}

E just renders a button, which is either disabled or not depending upon some "logic". And the "logic" is, well:

  1. For the E which is descendant of B, if the context value is "B" (disabledChildCtx), then the logic returns true, which disables the button in A -> B -> D -> E.
  2. For the E which is descendant of C, if the context value is "C", then the logic returns true, which disables the button in A -> C -> D -> E.

The problem is both the Es get same context, how can they decide uniquely if to render
or not a disabled button?

2

Answers


  1. Chosen as BEST ANSWER

    I figured out an answer to the problem. It would be better to create a context in its own file, say DisabledContext.js

    Then, B and C can be used as context providers with a unique boolean value in their respective cases according to the props being passed by the parent A. Then, the boolean value, which is the context value now, can be used by E to directly enable or disable the button.

    DisabledContext.js:

    const DisabledContext = React.createContext();
    export default DisabledContext;
    

    B.js

    const B = ({
      disabledChild //State variable in A which can be either "B" or "C"
    }) => {
      return 
      <DisabledChild.Provider value = {disabledChild === "B"}>
        <div>
          <D>
        </div>
      </DisabledChild.Provider>
    }
    

    C.js

    const C = ({
      disabledChild //State variable in A which can be either "B" or "C"
    }) => {
      return 
      <DisabledChild.Provider value = {disabledChild === "C"}>
        <div>
          <D>
        </div>
      </DisabledChild.Provider>
    }
    

    Now, no matter how many further common subchildren are there in , we can make the as a context consumer which simply uses the context value, irrespective of whose descendant it is.

    const E = () => {
      const isDisabled = useContext(DisabledChild);
      return 
      <button disabled = {isDisabled}> Button </button>
    }
    

  2. You need to pass a prop down from B -> D -> E and C -> D -> E, so that E knows what its grandparent is. Then E can do something like

    const E = ({grandparentCompName}) => {
      const ctxVal = useContext(disabledChildCtx);
      return <button disabled={grandparentCompName === ctxVal}></button>
    }
    

    If you are hoping to be able to do this for any arbitrary component tree without passing props, so that E can always just magically figure out what its grandparent is, there’s not a straightforward way to do that. See is there any way to access the parent component instance in React?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search