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: () => {}
});
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).
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.