Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
900 views
in Technique[技术] by (71.8m points)

reactjs - How to dispatch Redux action from stateless component when route is loaded?

Goal: when loading a react-router route, dispatch a Redux action requesting asynchronic Saga worker to fetch data for the underlying stateless component of that route.

Problem: stateless components are mere functions and don't have lifecycle methods, such as componentDidMount, so I can't(?) dispatch Redux action from inside the function.

My question is partly related to Converting stateful React component to stateless functional component: How to implement "componentDidMount" kind of functionality? , but my goal is to merely dispatch a single Redux action requesting data to be populated to the store asynchronously (I use Saga, but I think that's irrelevant to the problem, as my goal is to merely dispatch an ordinary Redux action), after which the stateless component would re-render due to the changed data prop.

I am thinking of two approaches: either use some feature of react-router, or Redux's connect method. Is there a so-called "React-way" to accomplish my goal?

EDIT: the only solution I have come up with so far, is dispatching the action inside mapDispatchToProps, this way:

const mapStateToProps = (state, ownProps) => ({
    data: state.myReducer.data // data rendered by the stateless component
});

const mapDispatchToProps = (dispatch) => {
    // catched by a Saga watcher, and further delivered to a Saga worker that asynchronically fetches data to the store
    dispatch({ type: myActionTypes.DATA_GET_REQUEST });
    return {};
};

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

However, this seems somehow dirty and not the correct way.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I don't know why you absolutly want a stateless component, while a stateful component with componentDidMount would do the job in a simple way.

Dispatching actions in mapDispatchToProps is very dangerous and may lead to dispatching not only on mount but whenever ownProps or store props changes. Side effects are not expected to be done in this method that should remains pure.

One easy way to keep your component stateless is to wrap it into an HOC (Higher-Order Component) that you could easily create:

MyStatelessComponent = withLifecycleDispatch(dispatch => ({
   componentDidMount: function() { dispatch({ type: myActionTypes.DATA_GET_REQUEST })};
}))(MyStatelessComponent)

Note that if you use Redux connect after this HOC, you can easily access dispatch from props directly as if you don't use mapDispatchToProps, dispatch is injected.

You can then do something very simple like:

let MyStatelessComponent = ...

MyStatelessComponent = withLifecycle({
   componentDidMount: () => this.props.dispatch({ type: myActionTypes.DATA_GET_REQUEST });
})(MyStatelessComponent)

export default connect(state => ({
   date: state.myReducer.data
}))(MyStatelessComponent);

HOC definition:

import { createClass } from 'react';

const withLifeCycle = (spec) => (BaseComponent) => {
  return createClass({
    ...spec,
    render() {
      return BaseComponent();
    }
  })
}

Here is a simple implementation of what you could do:

const onMount = (onMountFn) => (Component) => React.createClass({
   componentDidMount() {
     onMountFn(this.props);
   },
   render() { 
      return <Component {...this.props} />
   }  
});

let Hello = (props) => (
   <div>Hello {props.name}</div>
)

Hello = onMount((mountProps) => {
   alert("mounting, and props are accessible: name=" + mountProps.name)
})(Hello)

If you use connect around Hello component, they you can inject dispatch as props and use it instead of an alert message.

JsFiddle


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...