shaz
shaz

Reputation: 2387

React-Redux Counter example action firing twice

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

Upvotes: 6

Views: 10344

Answers (3)

theberbie
theberbie

Reputation: 94

If you're using mapDspatchToProps(), you don't need to add store.dspatch() as it's already implied.

const mapDispatchToProps  = {
  onIncrement: () => {
   return {
      type: 'INCREMENT'
    }},

  onDecrement: () => {
    return{
        type: 'DECREMENT'
    }},
}

Cheers

Upvotes: 2

elsyr
elsyr

Reputation: 796

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.

Upvotes: 7

Adam Kipnis
Adam Kipnis

Reputation: 10981

mapDispatchToProps() is already dispatching the actions. Adding store.dispatch() is redundant.

const mapDispatchToProps = {
  onIncrement: () => {
    return {
      type: 'INCREMENT'
    }
  },
  onDecrement: () => {
    return {
      type: 'DECREMENT'
    }
  },
}

Upvotes: 1

Related Questions