Search code examples
reactjsreact-context

React Context is not updating


I've switched from nesting props to my components into React's Context API. I've created a class to serve me some desired methods:

export default class StepDatabase {
  private readonly steps: Steps = steps;
  private currentStep: number = steps[0].step;

  public getStep(): Step {
    return this.steps[this.currentStep];
  }

  public nextStep(): void {
    if (this.currentStep === this.steps.length) return;
    this.currentStep++;
  }
}

And then, created a context:

const stepsInstance = new StepsDatabase();
export const StepsContext = createContext<StepsDatabase>(stepsInstance);

Of course, then provided it:

const App = () => (
    <div className={styles.App_container}>
      <main className={styles.App_grid}>
        <StepsContext.Provider value={stepsInstance}>
          <Sidebar />
          <Content />
        </StepsContext.Provider>
      </main>
    </div>
);

And tried using it in my Sidebar component:

const Sidebar = () => {
  const StepContext = React.useContext(StepsContext);
  const currentStep = StepContext.getStep();

  return (
    <section className={`${styles.App_gridItem} ${styles.App_sideItem}`}>
      <SidebarHeading>{currentStep.stepName}</SidebarHeading>
      <SidebarParagraph>{currentStep.stepDescription}</SidebarParagraph>

      <button onClick={() => StepContext.nextStep()}>step</button>
    </section>
  );
};

But the SidebarHeading and SidebarParagraph wasn't updating at all after clicking my button. The first step has worked fine. What's the problem?


Solution

  • There is nothing in your code that triggers the context to re-render. If the context does not re-render, it won't be able to trigger all of the components that consume it. You need something at a higher level to cause the context to re-render, or you need to pass a function within your context to your consumers that may trigger the re-render. See the documentation.

    Here is an example based upon your code:

    import React, { createContext, useState } from "react";
    import "./styles.css";
    
    const StepsContext = createContext();
    
    const Sidebar = () => {
      const { step, setNextStep } = React.useContext(StepsContext);
    
      return (
        <section>
          <div>Heading: {step.stepName}</div>
          <div>Paragraph: {step.stepDescription}</div>
    
          <button onClick={() => setNextStep()}>step</button>
        </section>
      );
    };
    
    export default function App() {
      const [steps, setSteps] = useState([
        { stepName: "Step 1", stepDescription: "My description 1" },
        { stepName: "Step 2", stepDescription: "My description 2" }
      ]);
      const [currentStep, setCurrentStep] = useState(0);
    
      return (
        <div>
          <main>
            <StepsContext.Provider
              value={{
                step: steps[currentStep],
                setNextStep: function () {
                  if (currentStep < steps.length - 1) {
                    setCurrentStep(currentStep + 1);
                  }
                }
              }}
            >
              <Sidebar />
              <div>Content</div>
            </StepsContext.Provider>
          </main>
        </div>
      );
    }