Challenges, Solutions, and Reflections Encountered with useContext and Provider
Tags: javascript, react, typescript, learning
Published: 2024-03-23 22:11:09

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>
```

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>
  );
```

ChildA.tsx
```
const ChildA = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>Child A</p>
      <div>{value}</div>
      <hr />
      <GrandChild />
      <hr />
    </>
  );
}
```

ChildB.tsx
```
const ChildB = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>Child B</p>
      <div>{value}</div>
      <hr />
    </>
  );
}
```

ChildC.tsx
```
const ChildC = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>Child C</p>
      <div>{value}</div>
      <hr />
    </>
  );
}
```

GrandChild.tsx
```
const GrandChild = () => {
  const value = useContext(ParentContext);
  return (
    <>
      <p>GrandChild</p>
      <div>{value}</div>
      <hr />
    </>
  );
}
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3fnwcpd0mefg0ekqwbk.jpeg)

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

`<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>
```
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.