How can I dispatch from child components in React

2020-06-03 05:57发布

问题:

My server has code like this:

<ReactRedux.Provider store={store}><Layout defaultStore={JSON.stringify(store.getState())}/></ReactRedux.Provider>

<Layout> obviously has more components which nest more.

I have a class like this deeper down:

import React from 'react';

export default React.createClass({
  render: function(){
    var classes = [
      'js-select-product',
      'pseudo-link'
    ];

    if (this.props.selected) {
      classes.push('bold');
    }

    return (
      <li className="js-product-selection">
        <span onClick={this.props.onClick} className={classes.join(' ')} data-product={this.props.id}>{this.props.name}</span>
      </li>
    );
  }
});

What I really want to do rather than this.props.onClick is dispatch an event to set state in a reducer. I've been some things online about context but I've gotten mixed reviews as that feature was or wasn't going away.

EDIT I see this connect method but I could have sworn I'd read not to use connect in children components.

回答1:

You're getting too caught up on children components. You should structure your app so that you have connected components and non-connected components. Non-connected components should be stateless, pure functions essentially, that take in all their requirements via props. Connected components should use the connect function to map redux state to props and redux dispatcher to props, and then be responsible for passing those props to child components.

You might have lots of connected components in an app, and lots of non-connected components. This post (by the creator of redux) discusses it in more detail, and talks about non-connected (dumb) components being responsible for actual display of UI, and connected (smart) components being responsible for composing non-connected components.

An example might be (using some newer syntax):

class Image extends React {
  render() {
    return (
      <div>
        <h1>{this.props.name}</h1>
        <img src={this.props.src} />
        <button onClick={this.props.onClick}>Click me</button>
      </div>
    );
  }
}

class ImageList extends React {
  render() {
    return (
      this.props.images.map(i => <Image name={i.name} src={i.src} onClick={this.props.updateImage} />)
    );
  }
}

const mapStateToProps = (state) => {
  return {
    images: state.images,
  };
};
const mapDispatchToProps = (dispatch) => {
  return {
    updateImage: () => dispatch(updateImageAction()),
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(ImageList);

In this example, ImageList is a connected component and Image is a non-connected component.



回答2:

There used to be advice to the effect to try to limit the components that you connect. See for example:

https://github.com/reactjs/redux/issues/419

https://github.com/reactjs/redux/issues/419#issuecomment-178850728

Anyway, that's really more useful for delegating a slice of state to a component. You can do that if it makes sense for your situation, or if you don't want to pass down a callback that calls dispatch() you can pass the store or dispatch down the hierarchy if you want.