Search code examples
reactjsjsxreact-context

Using React Context with same descendants


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?


Solution

  • 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>
    }