Home > OS >  React.useContext not updating
React.useContext not updating

Time:11-14

This is my first time using the React context hooks in an app and I am trying to set the SelectedBackgroundContext and it will not update. Console.log(typeof selectBackground) does appear as a function so I believe it is importing correctly and am not sure why it's not updating.

All of my code can be found in this CodeSandbox link below. The line I am running into issues with is child.js:8.

child

export default function Child() {
  const { selectedBackground } = useContext(SelectedBackgroundContext);
  const { selectBackground } = useContext(SelectedBackgroundContext);

  selectBackground(null); //Should render text saying "None" instead of image
  console.log(selectedBackground);

  const renderSelected = (context) => {
    if (context) {
      return (
        <img
          style={{ height: "200px" }}
          src={context}
          key={context   "Thumbnail"}
          alt={"thumbnail of "   context}
        />
      );
    } else {
      return <p>None</p>;
    }
  };

  return (
    <div>
      <p>Background:</p> {renderSelected(selectedBackground)}
    </div>
  );
}

context

export const SelectedBackgroundContext = React.createContext({
  selectedBackground:
    "https://lp-cms-production.imgix.net/2019-06/81377873 .jpg?fit=crop&q=40&sharp=10&vib=20&auto=format&ixlib=react-8.6.4",
  selectBackground: () => {}
});

Edit react-usecontext-not-updating

CodePudding user response:

The link below shows my preferred boilerplate for handling context (you can press the button to toggle the background on or off).

codesandbox link

The boilerplate has the following structure, which follows the philosophy of redux.

├── package.json
└── src
    ├── App.js
    ├── child.js
    ├── context
    │   ├── actions.js
    │   ├── reducer.js
    │   └── store.js
    └── index.js

action.js

export const actions = {
  SET_BACKGROUND: "SET_BACKGROUND"
};

It lists all the allowed action in the context.

reducer.js

import { actions } from "./actions.js";

export const reducer = (state, action) => {
  switch (action.type) {
    case actions.SET_BACKGROUND:
      return { ...state, background: action.background };
    default:
      return state;
  }
};

This is where actual change is made to the context. Reducer first check action type,. Then based on the action type, it makes corresponding modification to the context state.

store.js

import * as React from "react";
import { reducer } from "./reducer.js";
import { actions } from "./actions.js";

import { originalBackground } from "../child";

export const initialState = {
  background: originalBackground
};

export const AppContext = React.createContext();

export const Provider = (props) => {
  const { children } = props;
  const [state, dispatch] = React.useReducer(reducer, initialState);

  const value = {
    background: state.background,
    setBackground: React.useCallback(
      (val) => dispatch({ type: actions.SET_BACKGROUND, background: val }),
      []
    )
  };

  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};

This is where the context state is stored. The context state contains values (e.g. background) and functions to trigger the modification of such values (e.g. setBackground). Note that the function itself does not modify anything. It dispatches an action, which will be caught by the reducer for the actual state modification.

App.js

import React from "react";
import { Provider } from "./context/store";
import Child from "./child";

function App() {
  return (
    <Provider>
      <Child />
    </Provider>
  );
}

export default App;

Wrap the Provider over the component where the context is accessed. If the context is global to the entire app, Provider can wrap around App in index.js.

child.js

import React, { useContext } from "react";
import { AppContext } from "./context/store";

export const originalBackground =
  "https://lp-cms-production.imgix.net/2019-06/81377873 .jpg?fit=crop&q=40&sharp=10&vib=20&auto=format&ixlib=react-8.6.4";

export default function Child() {
  const { background, setBackground } = useContext(AppContext);

  const renderSelected = (context) => {
    if (context) {
      return (
        <img
          style={{ height: "200px" }}
          src={context}
          key={context   "Thumbnail"}
          alt={"thumbnail of "   context}
        />
      );
    } else {
      return <p>None</p>;
    }
  };

  const toggleBackground = () => {
    if (background) {
      setBackground(null);
    } else {
      setBackground(originalBackground);
    }
  };

  return (
    <div>
      <button onClick={toggleBackground}>
        {background ? "Background OFF" : "Background ON"}
      </button>
      <p>Background:</p> {renderSelected(background)}
    </div>
  );
}

This shows how context is used in a component.

  • Related