Redux: Calling store.dispatch() from a React component file?

Created on 19 Oct 2015  ·  10Comments  ·  Source: reduxjs/redux

I apologize if this is something that has already been cleared up, but a keyword search in the issues didn't turn up anything that helped.

If I want to call store.dispatch(someAction) from a component file, what is the correct way to access the store instance?

Right now I have the following, which works, but I'm not sure that it's the right approach:

index.jsx:

...
const store = applyMiddleware(
  loggingMiddleware, // log every action
  thunk // making API requests from actions
)(createStore)(reducer);

export {
  store
};
...

Controls.jsx

import {store} from './index.jsx';
import * as actions from './actions';
...
let clickHandler = function(node, data) {
  store.dispatch(actions.nodeClicked(data))
}
export const Controls = React.createClass({
  render: function() {
    // React component gets rendered, and it passes the clickHandler to a 
    // function that attaches it to a D3 visualization
  }
});

Is there anything wrong with exporting/importing the store like this? I think I could just create the click handlers as part of the React component and call this.context.store, but in reality there are about a dozen different event handlers that get passed to the D3 visualization, so that's not really practical in this situation.

question

Most helpful comment

Is there anything wrong with exporting/importing the store like this?

This won't work if you render your app on server because you want to have different store instance per request on server. That's why we never recommend this approach in the docs.

Why not do this instead?

import { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions';

let createHandlers = function(dispatch) {
  let onClick = function(node, data) {
    dispatch(actions.nodeClicked(data))
  };

  return {
    onClick,
    // other handlers
  };
}

class Controls extends Component {
  constructor(props) {
    super(props);
    this.handlers = createHandlers(this.props.dispatch);
  }

  render() {
    // pass this.handlers anywhere you'd like
  }
}

export default connect()(Controls);

All 10 comments

Check out https://github.com/rackt/react-redux for a helper method to bind dispatch to your components

Specifically, from https://redux.js.org/docs/basics/UsageWithReact.html

Any component wrapped with connect() call will receive a dispatch function as a prop, and any state it needs from the global state.

  • Updated comment with new link to relevant page, where the content no longer exists

Is there anything wrong with exporting/importing the store like this?

This won't work if you render your app on server because you want to have different store instance per request on server. That's why we never recommend this approach in the docs.

Why not do this instead?

import { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions';

let createHandlers = function(dispatch) {
  let onClick = function(node, data) {
    dispatch(actions.nodeClicked(data))
  };

  return {
    onClick,
    // other handlers
  };
}

class Controls extends Component {
  constructor(props) {
    super(props);
    this.handlers = createHandlers(this.props.dispatch);
  }

  render() {
    // pass this.handlers anywhere you'd like
  }
}

export default connect()(Controls);

@gaearon Looks great, thank you!

@gaearon Actually, after trying it out, I'm getting undefined for this.props.dispatch.

The relevant parts of my code:

import React from 'react';
import {Component} from 'react';
import {connect} from 'react-redux';
import * as vizActions from './vizActions';
import viz from './lib/viz';

let createHandlers = function(dispatch) {
  return {
    click: function(DOMNode, data) {
        // Getting undefined here
        dispatch(vizActions.setSelectedNode(data));
    }
  };
}

class Viz extends Component {
  constructor(props) {
    super(props);
    console.log(this.props.dispatch); // <- Getting undefined here
    // Passing the redux dispatch to be used in handlers function
    this.handlers = createHandlers(this.props.dispatch);
  }

  componentDidMount() {
    // Create Viz
  }

  componentDidUpdate() {
    // Update Viz Data, passing in this.handlers
  }

  render() {
    return <div id="viz"></div>;
  }
};


function mapStateToProps(state) {
  return {
    // Pass the network used for the viz as a prop
    network: state.get('visualization').get('network')
  };
}

export const VizContainer = connect(mapStateToProps, vizActions)(Viz);

However, after reading another issue (https://github.com/rackt/redux/issues/239) I realized that I am actually binding my actions to this.props via connect(), so I can just pass the necessary action when I call createHandlers(). Maybe this is not the best approach but it works now:

...
let createHandlers = function(action) {
  return {
    click: function(DOMNode, data) {
                // This works now
        action(data);
    }
  };
}

class Viz extends Component {
  constructor(props) {
    super(props);
    // Passing the redux dispatch to be used in handlers function
    this.handlers = createHandlers(this.props.setSelectedNode); // passing action creator function
  }
...

Actually, after trying it out, I'm getting undefined for this.props.dispatch.

Because you are not running the code I posted. In the code I posted connect did not receive second paramater. You pass vizActions there. That's why you don't get dispatch. If you remove vizActions from connect call you will get it.

Both approaches are equivalent though so go with what reads best to you.

:+1: Ok, that makes sense now. Thanks for the help!

Kinda late to the party, but replying just in case it helps someone later (including myself).
If a custom mapDispatchToProps is needed (the second param passed to connect), explicitly add dispatch as one of the keys.

const mapDispatchToProps = (dispatch) => {
  return {
    dispatch,

    // ... other custom mapped dispatch functions ...
    myFunction: (myParam) => {
      dispatch(State.Actions...)
    },
  }
}

export default connect( mapStateToProps, mapDispatchToProps )(Viz);

Hi. I also have this problem.
@gaearon , i'm grateful for your guidance. I've used your solutions, but I can not understand and use them.
I do not know how to use this.handlers When the user clicks on a card, I send that card id to the parent's components and run the action.

import React , { Component } from 'react';
import { Col } from "react-bootstrap";
import { connect } from 'react-redux';
import Home from "../components/Home";
import { fetchHome, asideArticle } from "../actions/index";

let createHandlers = function(dispatch) {
    let onClick = function(node, articleId) {
        dispatch(asideArticle(articleId))
    };
    return {
        onClick
    };
}

class HomeContainer extends Component {
    constructor(props) {
        super(props);
        this.handlers = createHandlers(this.props.dispatch); 
        console.log('constructor => this.props.dispatch => Result => dispatch is undefined);
    }
    componentDidMount() {
        this.props.fetchHome();
    }

    render() {
        const { homeData, article } = this.props;


       // 
        return (
            <Col>
                <Home homeData={homeData} asideArticle={(articleId) => asideArticle(articleId) } />
            </Col>
        );
    }
}

const mapStateToProps = state => ({
    homeData : state.home,
    article: state
});

function loadData(store){
    return store.dispatch(fetchHome());
}

export default {
    loadData,
    component: connect(mapStateToProps, { fetchHome, asideArticle })(HomeContainer)
}

How can I use this.handlers and pass the articleId after passing the click event to pass it ?
And what does the node parameter mean in the function(node, articleId) and what does it do?

@EhsanFahami :

This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux where there are a lot more people ready to help you out - you'll probably get a better answer faster. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jimbolla picture jimbolla  ·  3Comments

mickeyreiss-visor picture mickeyreiss-visor  ·  3Comments

timdorr picture timdorr  ·  3Comments

CellOcean picture CellOcean  ·  3Comments

elado picture elado  ·  3Comments