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.

Top comments (0)