title: reactJs redux: how to dispatch an action on a user event and link actions with the reducers and the store
body: <p>Being pretty new to reactJs &amp; redux and despite the tutorials (including the todolist example from redux), I still have difficulties understanding how to actually trigger an action which will change the state.</p>
  
  <p>I have already built something quite simple which is loading fine. Can somebody help me dispatch an action and that ends up in the store data being changed ?</p>
  
  <p>I'd like the togglePriceModule function to be called when the user clicks on li.module .
  Do I need to call a function of the main Pricing component passed as a prop to the child? What is the right way to do it?</p>
  
  <p>Thanks a lot !</p>
  
  <p>my app.js :</p>
  
  <pre><code>//Importing with braces imports a specific export of the file
  import { createDevTools } from 'redux-devtools'
  //Importing without braces imports the default export of the file
  import LogMonitor from 'redux-devtools-log-monitor'
  import DockMonitor from 'redux-devtools-dock-monitor'
  
  import React from 'react'
  import ReactDOM from 'react-dom'
  //Redux helps manage a single state which can be updated through actions call pure reducers
  //Importing muliple items injects them into the current scope
  import { applyMiddleware, compose, createStore, combineReducers } from 'redux'
  import { Provider } from 'react-redux'
  //React-router helps switch between components given a specific route
  import { Router, Route, Link } from 'react-router'
  import createHistory from 'history/lib/createHashHistory'
  import { syncHistory, routeReducer } from 'react-router-redux'
  
  //Imports an object of elements correspondings to every export of the file
  import * as reducers from './reducers';
  
  import Pricing from './components/pricing/pricing_main.js';
  
  const history = createHistory();
  const middleware = syncHistory(history);
  const reducer = combineReducers({
      ...reducers,
      routing: routeReducer
  });
  
  const DevTools = createDevTools(
      &lt;DockMonitor toggleVisibilityKey="ctrl-q"
                   changePositionKey="ctrl-alt-q"
                   defaultIsVisible={false}&gt;
          &lt;LogMonitor theme="tomorrow" preserveScrollTop={false} /&gt;
      &lt;/DockMonitor&gt;
  );
  
  const finalCreateStore = compose(
      applyMiddleware(middleware),
      DevTools.instrument()
  )(createStore);
  
  const store = finalCreateStore(reducer);
  
  middleware.listenForReplays(store);
  
  var renderComponent = function(component, id) {
      var reactContainer = document.getElementById(id);
      if (null !== reactContainer) {
          ReactDOM.render(
              &lt;Provider store={store}&gt;
                  &lt;div&gt;
                      &lt;Router history={history}&gt;
                          &lt;Route path="/" component={component} /&gt;
                      &lt;/Router&gt;
                      &lt;DevTools /&gt;
                  &lt;/div&gt;
              &lt;/Provider&gt;,
              reactContainer
          );
      }
  };
  
  renderComponent(Pricing, 'react-pricing');
  </code></pre>
  
  <p>My pricing components :</p>
  
  <pre><code>import React from 'react';
  var _ = require('lodash');
  
  const Pricing = React.createClass({
      getInitialState: function(){
          return {
              modules: {
                  'cms' : {
                      title: 'Fiches techniques &amp; Mercuriale',
                      subtitle: 'Gérez votre connaissance sous un format structuré',
                      price: 15,
                      details: [
                          'première ligne',
                          'deuxième ligne',
                          'troisième ligne'
                      ],
                      'activated': true
                  },
                  'cycle' : {
                      title: 'Cycle de menus',
                      subtitle: 'Programmez votre production dans le temps',
                      price: 20,
                      details: [
                          'première ligne',
                          'deuxième ligne',
                          'troisième ligne'
                      ],
                      'activated': false
                  },
                  'organigram' : {
                      title: 'Organigramme de production',
                      subtitle: "Optimisez l'affectation de votre main d'oeuvre",
                      price: 20,
                      details: [
                          'première ligne',
                          'deuxième ligne',
                          'troisième ligne'
                      ],
                      'activated': false
                  },
                  'teams' : {
                      title: 'Planning des équipes',
                      subtitle: "Gérez les temps de présence de vos salariés",
                      price: 20,
                      details: [
                          'première ligne',
                          'deuxième ligne',
                          'troisième ligne'
                      ],
                      'activated': false
                  },
                  'orders' : {
                      title: 'Commandes et stocks',
                      subtitle: "Commandez en un clic auprès de vos fournisseurs",
                      price: 20,
                      details: [
                          'première ligne',
                          'deuxième ligne',
                          'troisième ligne'
                      ],
                      'activated': false
                  }
              },
              options : {
                  users: {
                      title: "Nombre d'utilisateurs",
                      subtitle: "Distribuez des accès sécurisés",
                      price: 5,
                      unit: "5€ par utilisateur",
                      type: 'quantity',
                      value: 1
                  },
                  sites: {
                      title: 'Sites de vente ou de production',
                      subtitle: "Gérez vos multiples sites dans la même interface",
                      unit: "50€ par site",
                      type: 'quantity',
                      value: 1
                  },
                  backup: {
                      title: 'Sauvegarde',
                      subtitle: "Recevez une copie Excel de vos données tous les jours",
                      type: 'switch',
                      value: 'day'
                  }
              }
          }
      },
      componentWillMount: function(){
          this.setState(this.getInitialState());
      },
      render: function () {
          return (
              &lt;div className="wrapper"&gt;
                  &lt;h1&gt;Paramétrez votre offre&lt;/h1&gt;
                  &lt;div id="elements"&gt;
                      &lt;ul id="module-container" className="flex-container col"&gt;
                          {_.map(this.state.modules, function(module, key) {
                              return &lt;Module key={key} data={module} /&gt;
                          })}
                      &lt;/ul&gt;
                      &lt;ul id="param_container"&gt;
                          {_.map(this.state.options, function(option, key) {
                              return &lt;Option key={key} data={option} /&gt;
                          })}
                      &lt;/ul&gt;
                  &lt;/div&gt;
                  &lt;div id="totals" className="flex-container sp-bt"&gt;
                      &lt;span&gt;Total&lt;/span&gt;
                      &lt;span&gt;{calculatePrice(this.state)}&lt;/span&gt;
                  &lt;/div&gt;
              &lt;/div&gt;
          );
      }
  });
  
  function calculatePrice(state) {
      var modulePrices = _.map(state.modules, function(item){
          return item.price;
      });
      modulePrices = _.sum(modulePrices);
  
      return modulePrices;
  }
  
  var Module = React.createClass({
      render: function(){
          var data = this.props.data;
          return &lt;li className="module"&gt;
              &lt;div className="selection"&gt;
                  &lt;i className={data.activated ? 'fa fa-check-square-o' : 'fa fa-square-o'} /&gt;
              &lt;/div&gt;
              &lt;div className="title"&gt;
                  &lt;h3&gt;{data.title}&lt;/h3&gt;
                  &lt;h4&gt;{data.subtitle}&lt;/h4&gt;
              &lt;/div&gt;
              &lt;div className="price"&gt;
                  &lt;div className="figure"&gt;{data.price}&lt;/div&gt;
                  &lt;div className="period"&gt;par mois&lt;/div&gt;
              &lt;/div&gt;
              &lt;ul className="details"&gt;{
                  data.details.map(function(item, key){
                      return &lt;li key={key}&gt;&lt;i className="fa fa-check" /&gt;{item}&lt;/li&gt;
                  })}
              &lt;/ul&gt;
          &lt;/li&gt;
      }
  });
  
  var Option = React.createClass({
      render: function(){
          var data = this.props.data;
          return &lt;li className="param"&gt;
              &lt;div className="title"&gt;
                  &lt;h3&gt;{data.title}&lt;/h3&gt;
                  &lt;h4&gt;{data.subtitle}&lt;/h4&gt;
              &lt;/div&gt;
              &lt;div className="config"&gt;
                  &lt;span className="figure"&gt;&lt;i className="fa fa-minus" /&gt;&lt;/span&gt;
                  &lt;input value="1"/&gt;
                  &lt;span className="plus"&gt;&lt;i className="fa fa-plus" /&gt;&lt;/span&gt;
              &lt;/div&gt;
          &lt;/li&gt;
      }
  });
  
  export default Pricing;
  </code></pre>
  
  <p>my actions :</p>
  
  <pre><code>import { TOGGLE_PRICE_MODULE, INCREASE_PRICE_OPTION, DECREASE_PRICE_OPTION } from '../constants/constants.js'
  
  export function increasePriceOption(value) {
      return {
          type: INCREASE_PRICE_OPTION,
          value: value
      }
  }
  
  export function decreasePriceOption(value) {
      return {
          type: DECREASE_PRICE_OPTION,
          value: value
      }
  }
  
  export function togglePriceModule(activated) {
      return {
          type: TOGGLE_PRICE_MODULE,
          activated: activated
      }
  }
  </code></pre>
  
  <p>my reducers:</p>
  
  <pre><code>import { TOGGLE_PRICE_MODULE, INCREASE_PRICE_OPTION, DECREASE_PRICE_OPTION } from '../constants/constants.js'
  
  
  export default function updateModule(state = false, action) {
      if(action.type === TOGGLE_PRICE_MODULE) {
          return !state;
      }
      return state
  }
  
  export default function updateOption(state = 1, action) {
      if(action.type === INCREASE_PRICE_OPTION) {
          return state + 1;
      }
      else if(action.type === DECREASE_PRICE_OPTION) {
          if (state &lt; 2) {
              return 1;
          } else {
              return state + 1;
          }
      }
      return state
  </code></pre>
  
  <p><strong>EDIT 1</strong></p>
  
  <p>I've isolated the Module component and tried to adapt from the first answer below : the modules loads properly but there is no effect at all in the view. Missing something ?</p>
  
  <p>first error : 
  I needed to change </p>
  
  <blockquote>
    <p>import * as reducers from './reducers';</p>
  </blockquote>
  
  <p>into </p>
  
  <blockquote>
    <p>import * as reducers from './reducers/pricing.js';</p>
  </blockquote>
  
  <p>for a console log to actually show my reducers.</p>
  
  <p>Why?</p>
  
  <p>Second:
  A console.log shows that the action is indeed being called
  The same in the reducer shows it's not. 
  <strong>How should I make the link between the reducer and the action ?</strong>
  Should I use mapStateToProps  and connect somehow ?</p>
  
  <pre><code>import React from 'react';
  import { togglePriceModule } from '../../actions/pricing.js';
  
  var Module = React.createClass({
      handleClick: function(status){
          this.context.store.dispatch(togglePriceModule(status));
      },
      render: function(){
          console.log(this.props);
          var data = this.props.data;
          return &lt;li className="module" onClick={this.handleClick}&gt;
              &lt;div className="selection"&gt;
                  &lt;i className={data.activated ? 'fa fa-check-square-o' : 'fa fa-square-o'} /&gt;
              &lt;/div&gt;
              &lt;div className="title"&gt;
                  &lt;h3&gt;{data.title}&lt;/h3&gt;
                  &lt;h4&gt;{data.subtitle}&lt;/h4&gt;
              &lt;/div&gt;
              &lt;div className="price"&gt;
                  &lt;div className="figure"&gt;{data.price}&lt;/div&gt;
                  &lt;div className="period"&gt;par mois&lt;/div&gt;
              &lt;/div&gt;
              &lt;ul className="details"&gt;{
                  data.details.map(function(item, key){
                      return &lt;li key={key}&gt;&lt;i className="fa fa-check" /&gt;{item}&lt;/li&gt;
                  })}
              &lt;/ul&gt;
          &lt;/li&gt;
      }
  });
  
  Module.contextTypes = {
      store: React.PropTypes.object
  };
  
  export default Module;
      }
  </code></pre>
  
  <p><strong>EDIT2</strong></p>
  
  <p>I've made changes as suggested and the reducer is now called.
  However I have no UI change so I'm gessing I did something wrong. <strong>Is the way I am handling the state / the store / the prop right ?</strong></p>
  
  <p>The bundle is valid but I get the following error in the console :</p>
  
  <blockquote>
    <p>warning.js:45 Warning: setState(...): Cannot update during an existing
    state transition (such as within <code>render</code>). Render methods should be a
    pure function of props and state.</p>
  </blockquote>
  
  <p>Also, should I pass a function from the pricing component (a container) to the module component and put the logic above instead of dispatching the action in the child module component ?</p>
  
  <p>My updated module component on which I click in the hope of a UI change :</p>
  
  <pre><code>import React from 'react';
  import { connect } from 'react-redux';
  import { togglePriceModule } from '../../actions/pricing.js';
  
  var Module = React.createClass({
      handleClick: function(status){
          this.context.store.dispatch(togglePriceModule(status));
      },
      render: function(){
          var data = this.props.data;
          return &lt;li className="module" onClick={this.handleClick(!data.activated)}&gt;
              &lt;div className="selection"&gt;
                  &lt;i className={data.activated ? 'fa fa-check-square-o' : 'fa fa-square-o'} /&gt;
              &lt;/div&gt;
              &lt;div className="title"&gt;
                  &lt;h3&gt;{data.title}&lt;/h3&gt;
                  &lt;h4&gt;{data.subtitle}&lt;/h4&gt;
              &lt;/div&gt;
              &lt;div className="price"&gt;
                  &lt;div className="figure"&gt;{data.price}&lt;/div&gt;
                  &lt;div className="period"&gt;par mois&lt;/div&gt;
              &lt;/div&gt;
              &lt;ul className="details"&gt;{
                  data.details.map(function(item, key){
                      return &lt;li key={key}&gt;&lt;i className="fa fa-check" /&gt;{item}&lt;/li&gt;
                  })}
              &lt;/ul&gt;
          &lt;/li&gt;
      }
  });
  
  Module.contextTypes = {
      store: React.PropTypes.object
  };
  
  function mapStateToProps(state) {
      return {
          data: state.updateModule.data
      }
  }
  
  export default connect(mapStateToProps)(Module)
  
  export default Module;
  </code></pre>
  
  <p>My action :</p>
  
  <pre><code>export function togglePriceModule(status) {
      return {
          type: TOGGLE_PRICE_MODULE,
          activated: status
      }
  }
  </code></pre>
  
  <p>My reducer :</p>
  
  <pre><code>import { TOGGLE_PRICE_MODULE, INCREASE_PRICE_OPTION, DECREASE_PRICE_OPTION } from '../constants/constants.js'
  
  export function updateModule(state = {}, action) {
      console.log('updateModule reducer called');
      if(action.type === TOGGLE_PRICE_MODULE) {
          return {...state, activated : action.activated };
      }
      return state
  }
  
  export function updateOption(state = {}, action) {
      if(action.type === INCREASE_PRICE_OPTION) {
          return {...state, value: state.value + 1};
      } else if(action.type === DECREASE_PRICE_OPTION) {
          if (state.value &lt; 2) {
              return {...state, value : 1};
          } else {
              return {...state, value : state.value - 1};
          }
      }
      return state
  }
  </code></pre>