Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-Redux Counter example action firing twice

Tags:

react-redux

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();

double action

like image 389
shaz Avatar asked Dec 22 '17 22:12

shaz


1 Answers

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.

like image 57
elsyr Avatar answered Dec 03 '22 23:12

elsyr