Here is a CodeSandbox that has the example code below, and the linter highlights some of the issues:
Below is a basic example of what I'm trying to do. In a container ponent, I have a context AppContext
that is providing state to child ponents, <ChildConsumer />
and <ChildDispatcher />
.
The <ChildConsumer />
ponent is receiving this state using useContext
, and this seems to be working as expected.
Inside <ChildDispatcher />
, I'm trying to dispatch an action when a button is clicked. To do this, I've created a reducer reducer
that handles an action. I've also set up useReducer here that takes in reducer
and the initial store
state.
When I click the button, nothing happens. What I'm expecting to happen is that the dispatch
receives both the state
pulled off of useReducer
as well as an action
object, and passes these to the reducer. The reducer should see that the action of type BUTTON_CLICKED
was received, and should return a new state containing the old state as well as an additional 'goodbye'
item. Then, the child ponent <ChildConsumer />
should rerender with this new state.
import React, { createContext, useContext, useReducer } from "react";
import ReactDOM from "react-dom";
const store = [""];
const AppContext = createContext(store);
const ChildDispatcher = () => {
const reducer = (state, action) => {
switch (action.type) {
case "BUTTON_CLICKED":
return [...state, "goodbye"];
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, store);
const handleClick = () =>
dispatch(state, {
type: "BUTTON_CLICKED"
});
return <button onClick={handleClick}>press me</button>;
};
const ChildConsumer = () => {
const [consumer] = useContext(AppContext);
return <div>{consumer}</div>;
};
const App = () => {
return (
<div>
<h1>Using Context and useReducer</h1>
<AppContext.Provider value={["hello"]}>
<ChildConsumer />
<ChildDispatcher />
</AppContext.Provider>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is a CodeSandbox that has the example code below, and the linter highlights some of the issues: https://codesandbox.io/s/react-repl-bw2h1
Below is a basic example of what I'm trying to do. In a container ponent, I have a context AppContext
that is providing state to child ponents, <ChildConsumer />
and <ChildDispatcher />
.
The <ChildConsumer />
ponent is receiving this state using useContext
, and this seems to be working as expected.
Inside <ChildDispatcher />
, I'm trying to dispatch an action when a button is clicked. To do this, I've created a reducer reducer
that handles an action. I've also set up useReducer here that takes in reducer
and the initial store
state.
When I click the button, nothing happens. What I'm expecting to happen is that the dispatch
receives both the state
pulled off of useReducer
as well as an action
object, and passes these to the reducer. The reducer should see that the action of type BUTTON_CLICKED
was received, and should return a new state containing the old state as well as an additional 'goodbye'
item. Then, the child ponent <ChildConsumer />
should rerender with this new state.
import React, { createContext, useContext, useReducer } from "react";
import ReactDOM from "react-dom";
const store = [""];
const AppContext = createContext(store);
const ChildDispatcher = () => {
const reducer = (state, action) => {
switch (action.type) {
case "BUTTON_CLICKED":
return [...state, "goodbye"];
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, store);
const handleClick = () =>
dispatch(state, {
type: "BUTTON_CLICKED"
});
return <button onClick={handleClick}>press me</button>;
};
const ChildConsumer = () => {
const [consumer] = useContext(AppContext);
return <div>{consumer}</div>;
};
const App = () => {
return (
<div>
<h1>Using Context and useReducer</h1>
<AppContext.Provider value={["hello"]}>
<ChildConsumer />
<ChildDispatcher />
</AppContext.Provider>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Share
Improve this question
asked Mar 7, 2020 at 17:20
rpivovarrpivovar
3,45813 gold badges49 silver badges83 bronze badges
1
- How would you actually refactor this to place each child in its own file? – GDes00 Commented Jul 10, 2020 at 22:48
2 Answers
Reset to default 4I have made a small amendment in you code.
you have to pass the dispatch like below. Dispatch expects an argument of type object.
const handleClick = () => dispatch({ type: "BUTTON_CLICKED" });
And then this state could be accessed like this.
const ChildDispatcher = () => {
const reducer = (state, action) => {
switch (action.type) {
case "BUTTON_CLICKED":
//action.state // like this
return [...state, "goodbye"];
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, store);
const handleClick = () =>
dispatch(state, {
type: "BUTTON_CLICKED"
});
return <button onClick={handleClick}>press me</button>;
};
By default react will pass the state to the dispatcher. but if you want to pass some data the you can add it in the object and pass that object to dispatch.
const handleClick = () => dispatch({ type: "BUTTON_CLICKED", state: state });
CodeSandBox:
Few problems with this:
The ChildDispatch state is only available to ChildDispatch and will not affect upper level ponents. To change the context value, you need to provide a dispatch in that ponent and make a custom hook (or pass it as props) to use it in the ChildDispatch.
Don’t pass state when calling your dispatch. useReducer will handle that for you. Just send the action.
This is what it means that there should be a one way direction for your data flow. Parent ponents control the shared state / way to manage state, and child ponents use that to render / perform actions.