Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling store.dispatch() from a React component file? #916

Closed
alexklibisz opened this issue Oct 19, 2015 · 10 comments
Closed

Calling store.dispatch() from a React component file? #916

alexklibisz opened this issue Oct 19, 2015 · 10 comments
Labels

Comments

@alexklibisz
Copy link

@alexklibisz alexklibisz commented Oct 19, 2015

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.

@Nicktho
Copy link

@Nicktho Nicktho commented Oct 19, 2015

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

@nhagen
Copy link

@nhagen nhagen commented Oct 19, 2015

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

@gaearon
Copy link
Contributor

@gaearon gaearon commented Oct 19, 2015

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 gaearon closed this as completed Oct 19, 2015
@alexklibisz
Copy link
Author

@alexklibisz alexklibisz commented Oct 19, 2015

@gaearon Looks great, thank you!

@alexklibisz
Copy link
Author

@alexklibisz alexklibisz commented Oct 20, 2015

@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 (#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
  }
...

@gaearon
Copy link
Contributor

@gaearon gaearon commented Oct 20, 2015

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.

@alexklibisz
Copy link
Author

@alexklibisz alexklibisz commented Oct 20, 2015

👍 Ok, that makes sense now. Thanks for the help!

@peacechen
Copy link

@peacechen peacechen commented Jun 13, 2017

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

@fahamidev
Copy link

@fahamidev fahamidev commented Feb 26, 2018

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?

@markerikson
Copy link
Contributor

@markerikson markerikson commented Feb 26, 2018

@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!

@reduxjs reduxjs locked as resolved and limited conversation to collaborators Feb 26, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

7 participants