rootReducer.js
import { combineReducers } from "redux";
import counterReducer from "./counter.reducer";
const rootReducer = combineReducers({
counter: counterReducer
})
export default rootReducer
counterReducer.js
import { DECREMENT_COUNTER, INCREMENT_COUNTER } from "../Constants";
import initialState from "../initialState";
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT_COUNTER: {
return {
...state,
counter: state.counter + 1
}
}
case DECREMENT_COUNTER:
return {
...state,
counter: state.counter - 1
}
default:
return state
}
}
export default counterReducer
App.js
import React from 'react';
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { decrementCounter, incrementCounter } from './actions/counter.action';
function App() {
const selector = useSelector(state => state)
console.log(selector)
const dispatch = useDispatch()
return (
<div>
<h1>Counter: {selector.counter}</h1>
<button onClick={() => dispatch(incrementCounter())}>Increment</button>
<button onClick={() => dispatch(decrementCounter())}>Decrement</button>
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
reportWebVitals();
store.js
import { configureStore } from "@reduxjs/toolkit";
import initialState from "./initialState";
import rootReducer from "./reducers/root.reducer";
const store = configureStore({
reducer: rootReducer,
preloadedState: initialState
})
export default store
initialState.js
export const initialState = {
counter: 0
}
So I'm trying to implement counter with increment and decrement button, but when I click on increment or decrement button it throughs me error stating
Objects are not valid as a React child (found: object with keys {counter}).
I've tried console logging the state and I found that initially my state looks like
counter: 0
But when I click on the increment or decrement button state becomes
counter: {
counter: NaN
}
I'm not able to figure out why this is happening.
If the state is initially counter: 0
then I suspect the initialState
of the counterReducer
is actually just something like const initialState = 0;
. It would seem then the problem is your counterReducer
function is injecting a nested counter
property.
state.counter
is undefined, and when you add/subtract it results in a NaN
, and then all further math operations on NaN
are NaN
.
const state = 0;
console.log({ state, "state.counter + 1": state.counter + 1 });
Once state.counter
is mutated with the nested property, it becomes state.counter.counter
. It's this counter.counter
object that can't be rendered as JSX.
Update counterReducer
increment/decrement cases to add to or subtract from just state
which is the counter value.
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT_COUNTER:
return state + 1;
case DECREMENT_COUNTER:
return state - 1;
default:
return state;
}
}
In the UI you'll want to select the specific state you want to subscribe to. Don't write selector functions that return the entire state, e.g. don't use useSelector(state => state);
.
function App() {
const counter = useSelector(state => state.counter);
const dispatch = useDispatch();
return (
<div>
<h1>Counter: {counter}</h1>
<button onClick={() => dispatch(incrementCounter())}>
Increment
</button>
<button onClick={() => dispatch(decrementCounter())}>
Decrement
</button>
</div>
);
}