0

I was trying to use React's context and I faced the following issues. I am new to react, so bear with me and would appreciate if you can direct me to a useful resource

  1. Does a component has to be wrapped inside of a provider to be able to access the values of the context? like will the following childComponent be able to access the context even if it's not wrapped inside of a provider like
<ParentComponentProvider>
     <ChildComponent />
</ParentComponentProvider>
import React, { createContext, useContext } from 'react';

const MyContext = createContext();

const ParentComponentProvider = ({ children }) => {
 return (
   <MyContext.Provider value="Hello from context">
     {children}
   </MyContext.Provider>
 );
};

const ChildComponent = () => {
 const contextValue = useContext(MyContext);
 return <div>{contextValue}</div>;
};

const App = () => {
 return (
   <div>
       <ChildComponent />
   </div>
 );
};
  1. if it's possible to access the context values without being direclty wrapped inside of the provider, then what is the point of wrapping a component or components inside of a provider wrapper?
  2. Does a change in a value of a context affect the components that are wrapped inside of a provider to re-render? what if I don't wrap the components that use the contextvalues inside of a provider, will they re-render?
JustDev
  • 37
  • 4

1 Answers1

1

Does a component has to be wrapped inside of a provider to be able to access the values of the context?

To access the value passed to <MyContext.Provder>, yes

will the following childComponent be able to access the context even if it's not wrapped inside of a provider

You would only be able to access any default context values, ie the value you pass to createContext()...

const MyContext = createContext("This is the default message");

Does a change in a value of a context affect the components that are wrapped inside of a provider to re-render?

If those components use the context via the useContext() hook, then yes; the hook triggers a re-render when changes are detected.

If there are wrapped components that do not use the hook, they will not. This is why it's generally ok to wrap your entire application in a context provider

what if I don't wrap the components that use the contextvalues inside of a provider, will they re-render?

No. Again, they will only be able to access the default context value which is not changing.


Consider the following context and provider

const MyContext = createContext({ value: "Default context value"});

const MyContextProvider = ({ children }) => {
  const [value, setValue] = useState("Initial context value");

  return (
    <MyContext.Provider value={{ value, setValue }}>
      {children}
    </MyContext.Provider>
  );
};
  • Components outside <MyContextProvider> will only see "Default context value"
  • Components inside the provider will see "Initial context value"
  • If a wrapped component calls setValue() with a new value, it and the other wrapped consumers will re-render and see the new value.

Here's a more involved demo showing what happens

const MyContextConsumer = ({ id }) => {
  const { value } = useContext(MyContext);

  console.log(`[MyContextConsumer::${id}] Render!`);

  return <p>{value}</p>;
};

const MyContextUpdater = () => {
  const { setValue } = useContext(MyContext);

  console.log("[MyContextUpdater] Render!");

  return (
    <button onClick={() => setValue(`New value ${Date.now()}`)}>
      Update context value
    </button>
  );
};

const SomeComponent = () => {
  console.log("[SomeComponent] Render!");
  return <p>Static content</p>;
};

with a top-level like this

<MyContextProvider>
  <MyContextConsumer id="in-provider" />
  <MyContextUpdater />
  <SomeComponent />
</MyContextProvider>

<MyContextConsumer id="outside-provider" />

You would see on initial render

[MyContextConsumer::in-provider] Render!
[MyContextUpdater] Render!
[SomeComponent] Render!
[MyContextConsumer::outside-provider] Render!

On clicking the button, you would then see

[MyContextConsumer::in-provider] Render!
[MyContextUpdater] Render!

Edit nostalgic-golick-jqf8lw

Phil
  • 157,677
  • 23
  • 242
  • 245
  • Thanks for the detailed response and it is perfect. I have one more question, though. In the react official documentation, it states the following "React automatically re-renders all the children that use a particular context starting from the provider that receives a different value. The previous and the next values are compared with the Object.is comparison. Skipping re-renders with memo does not prevent the children receiving fresh context values.", in what scenarios does this help or what is the point of using memo, if a new values aren't displayed to end users? – JustDev Jun 20 '23 at 07:29
  • 1
    @JustDev it's telling you that using `memo()` **won't** prevent context consumers from re-rendering when context values change. From [the docs](https://react.dev/reference/react/memo#reference)... _"This memoized version of your component will usually not be re-rendered when its parent component is re-rendered as long as its props have not changed. But React may still re-render it"_. I've updated the CodeSandbox with an example – Phil Jun 20 '23 at 23:22