javascriptreactjsreact-hooksreact-contextcreatecontext

UseContexts and Createcontex in React


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?

Solution

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

    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