DEV Community

Ayako yk
Ayako yk

Posted on

Challenges, Solutions, and Reflections Encountered with useContext and Provider

While refactoring a project, I encountered difficulty obtaining values. Despite familiarizing myself with useContext and Provider, I encountered challenges with managing parent/child structures, particularly as the code grew longer and more complex. Reflecting on this now, I realize that the issue I faced was quite fundamental. To prevent similar challenges in the future, I will summarize the mistakes I made and their corresponding solutions.

Topic:

  1. useContext and Provider
  2. Rendering Rule
  3. Challenges Encountered During Refactoring

useContext and Provider:
Components within the provider can access the value without the need for prop passing.

<ParentProvider>
  <ChildA />
  <ChildB />
  <ChildC />
</ParentProvider>
Enter fullscreen mode Exit fullscreen mode

ParentProvider.tsx

type ParentProviderProps = {
  children: ReactNode;
};

export const ParentContext = createContext<string>('');

export const ParentProvider = ({ children }: ParentProviderProps) => {
  const [value, setValue] = useState<string>('example');

  return (
    <ParentContext.Provider value={value}>{children}</ParentContext.Provider>
  );
Enter fullscreen mode Exit fullscreen mode

ChildA.tsx

const ChildA = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>Child A</p>
      <div>{value}</div>
      <hr />
      <GrandChild />
      <hr />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

ChildB.tsx

const ChildB = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>Child B</p>
      <div>{value}</div>
      <hr />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

ChildC.tsx

const ChildC = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>Child C</p>
      <div>{value}</div>
      <hr />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

GrandChild.tsx

const GrandChild = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>GrandChild</p>
      <div>{value}</div>
      <hr />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Image description

React checks whether the value within the provider has changed. If it has, the consuming components must be updated accordingly.

Rendering Rule:
When a value changes, React renders recursively from top to bottom, updating parents first and then their children, such as Child A, Child B, and so on.

Even if Child A doesn't directly consume the value from the provider, it will still render due to its parent's update.
--> This aspect could be explored in more depth.

The problem I encountered was as follows:

export const Page = () => {
  //...
  <xxxProvider>
    <yyyProvider>
      <DeleteBtn />
      <DeletePopUp />
    </yyyProvider>
  </xxxProvider>
}
Enter fullscreen mode Exit fullscreen mode

<DeletePopUp />contained values and dispatched actions from useContext(). Since both <DeleteBtn /> and <DeletePopUp /> were child components of <Page>, whenever there were changes in the state, the values were promptly updated and made available in the child components.

Refactoring:
I needed to refactor it and move all the functions inside <DeletePopUp /> to <Page>. Then, const zzz = useContext(xxxDispatchContext) returned undefined.
Now, const zzz = useContext(xxxDispatchContext) is in the parent component and is outside the scope of the provider, so <DeletePopUp /> no longer has direct access to the context provided by <xxxProvider>.

<Page> // const zzz = useContext(xxxDispatchContext) is now up here
  <xxxProvider>
    <yyyProvider>
      <DeleteBtn />
      <DeletePopUp /> // const zzz = useContext(xxxDispatchContext) was here
    </yyyProvider>
  </xxxProvider>
</Page>
Enter fullscreen mode Exit fullscreen mode

When I call const zzz = useContext(xxxProvider) inside <Page />, it tries to access the value provided by <xxxProvider>. However, if <Page /> is outside the scope of <xxxProvider>, then zzz will be undefined because there's no provider higher up in the component tree providing the xxxProvider context.

If I had been able to simplify the structure and create the image above, I would have solved the issue much faster. In the actual code, the Page component was a grandchild component, and the separate DeletePopUp, though it shared the same name, was a global component that I also needed to modify to pass the values. The key here is to break problems into small pieces and solve them one by one.

During my research, I encountered another curious topic: rendering and reconciliation. Familiarizing myself with this will be my next step.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay