https://codesandbox.io/s/jvj6o043yv
I'm trying to grok the basics of react-redux connect, Provider, and mapStateToProps and MapDispatchToProps using the very simple Counter example. The point of the exercise is to have the props and actions injected into the Counter component.
The issue I'm seeing is that my actions are firing twice on each button click. I'm a Redux noob so I'm sure (I hope) it's a fairly basic error. I'd appreciate any pointers to where I went wrong. Thanks in advance.
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { connect } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
// Reducer
const counter = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// store setup
const configureStore = () => {
const middlewares = [];
if (process.env.NODE_ENV !== 'production') {
middlewares.push(createLogger());
}
return createStore(
counter,
applyMiddleware(...middlewares)
);
};
const store = configureStore();
// functional component
let Counter = ({
currCounter,
onIncrement,
onDecrement
}) => (
<div>
<h1>{currCounter}</h1>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
</div>
);
// Connect logic
const mapStateToProps = (state) => ({
currCounter: state
})
const mapDispatchToProps = {
onIncrement: () =>
store.dispatch({
type: 'INCREMENT'
}),
onDecrement: () =>
store.dispatch({
type: 'DECREMENT'
}),
}
Counter = connect(
mapStateToProps,
mapDispatchToProps
)(Counter)
// Entry point
const render = () => {
ReactDOM.render(
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
);
};
store.subscribe(render);
render();
The reason you're getting a duplicated dispatch is because of the way you've written your mapDispatchToProps
object (and seemingly innocent use of double arrow syntax).
Double arrow syntax without the braces, as in() => value
, translates into function { return value }
.
Therefore, onIncrement
isn't actually a function that looks like { store.dispatch(...) }
anymore - it's actually the return value of the dispatch call. In this case, that's simply the dispatched action.
If we write onIncrement
to look something like this (which just returns the action object):
onIncrement: () => {
return {
type: "INCREMENT"
}
}
we end up with a properly dispatched action on button presses.
That's causing your double dispatch - onIncrement
is first calling store.dispatch
and then spinning another dispatch from the returned object.
Regardless, you can solve this pretty simply by adding braces to your onIncrement()
:
onIncrement: () => {
store.dispatch({
type: 'INCREMENT'
});
},
You can also simply return just the action object, as shown earlier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With