简体   繁体   English

Redux & React Router:结合调度和导航(history.push)

[英]Redux & React Router: Combing dispatch and navigation (history.push)

I'm a bit clueless as to how to use React Router's history.push('/route') with Redux .我对如何在Redux使用 React Router 的history.push('/route')有点无能为力。

In other words, if you connect a component with Redux's mapDispatchToProps , how do you put history.push('/route') that is supposed to be fired after dispatching a certain action?换句话说,如果你用 Redux 的mapDispatchToProps连接一个组件,你如何放置history.push('/route') ,它应该在调度某个动作后被触发?

To clarify the issue, here is a snippet of my experimentation...为了澄清这个问题,这是我的实验片段......

class PostContainer extends React.Component {

    handleSubmit(data) {
        return this.props.addPost(data);
    }

    render() {
        return (<Post onSubmit={data=> this.handleSubmit(data)}/>);
    }
}

const mapDispatchToProps = (dispatch) => {
    return {    
        addPost: (data) => {
            dispatch(addPost(data)).data.then((result) => {
                dispatch(addPostFulfilled(result.data));

                //How do you call the following function?
                this.props.history.push('/posts')
            }).catch((error) => {
                dispatch(addPostRejected());
            });
        },      
    }
}

export default connect(null, mapDispatchToProps)(PostContainer);

As you can see, as soon as a post is added into Redux's state through addPostFulfilled , the page needs to move to /posts via history.push.如您所见,只要通过addPostFulfilled将帖子添加到 Redux 的状态,页面就需要通过 history.push 移动到/posts

According to Redux's doc, containers are made for updating states (as well as fetching data).根据 Redux 的文档,容器用于更新状态(以及获取数据)。 So, dumb components are not supposed to put anything like history.push .因此,愚蠢的组件不应该放置诸如history.push东西。

What would be a better approach than the code above?有什么比上面的代码更好的方法?

Any advice will be appreciated.任何建议将被认真考虑。

You can either user react-router-redux (require an initial set up) and dispatch your action from action creators 您可以使用用户react-router-redux(需要初始设置)并从操作创建者发送您的操作

import { push } from 'react-router-redux'
class PostContainer extends React.Component {

  handleSubmit(data) {
    return this.props.addPost(data);
  }

  render() {
    return (
      <div>
        <Post onSubmit={data=> this.handleSubmit(data)}/>
      </div>
    );
  }
}

const mapDispatchToProps = (dispatch) => {
  return {    
    addPost: (data) => {
      dispatch(addPost(data)).data.then((result) => {
        dispatch(addPostFulfilled(result.data));

        //How do you call the following function?
        dispatch(push('/posts'));
      }).catch((error) => {
        dispatch(addPostRejected());
      });
    },      
  }
}
export default connect(null, mapDispatchToProps)(PostContainer);

or just pass push as a "done callback". 或者只是将推送作为“完成回调”。

class PostContainer extends React.Component {

  handleSubmit(data, done) {
    return this.props.addPost(data, done);
  }

  render() {
    const { push } = this.props.history;
    return (
      <div>
        <Post onSubmit={data=> this.handleSubmit(data, push)}/>
      </div>
    );
  }
}

const mapDispatchToProps = (dispatch) => {
  return {    
    addPost: (data, done) => {
      dispatch(addPost(data)).data.then((result) => {
        dispatch(addPostFulfilled(result.data));

        //How do you call the following function?
        done('/posts')
      }).catch((error) => {
        dispatch(addPostRejected());
      });
    },      
  }
}

export default connect(null, mapDispatchToProps)(PostContainer);

IMHO , you can do a history.push even from your dumb components,ie 恕我直言,你甚至可以从愚蠢的组件中做一个history.push ,即

const mapDispatchToProps = (dispatch) => {
    return {    
        addPost: (data) => {
            dispatch(addPost(data)).data.then((result) => {
                dispatch(addPostFulfilled(result.data));
                <!-- removed from here -->
            }).catch((error) => {
                dispatch(addPostRejected());
            });
        },      
    }
}

Once your addPostFulFilled data is done you have the option of doing your this.history.push('/route') in your component itself. 完成addPostFulFilled数据后,您可以选择在组件本身中执行this.history.push('/route') You can do it any of the lifefcycle methods which is called when the props update. 您可以执行任何在props更新时调用的生命周期方法。 Perhaps, add it to componentWillReceiveProps . 也许,将它添加到componentWillReceiveProps

class PostContainer extends React.Component {
  componentWillReceiveProps(nextProps){
  /*Added here*/
  if(nextProps.someFlag){ this.props.history.push('/someRoute');
}}

Note that the above works only if 请注意,上述内容仅适用于

  1. You are Ok to not track location changes via the store .( Read More ) 您可以通过store跟踪位置更改。( 阅读更多
  2. You are using react-router-4 in which case you will be wrapping your connect with withRouter which will enable the history object to be present as a prop in the PostContainer . 您正在使用react-router-4在这种情况下,您将使用withRouter包装连接,这将使历史对象在PostContainer作为prop PostContainer
  3. Care should be taken to prevent unnecessary rerenders and write necessary logic in shouldComponentUpdate . 应注意防止不必要的重新渲染并在shouldComponentUpdate编写必要的逻辑。

If the above sounds like a whole lot of gibberish , you can always go with react-router-redux 's push API which enable pushing new entries in browser history. 如果以上听起来像是一大堆乱码,你可以随时使用react-router-reduxpush API,它可以在浏览器历史记录中推送新条目。 You can import that in any place of your choice and just push a new route. 您可以在您选择的任何地方导入,只需推送新路线即可。

Details can be seen here in the react-router-redux github pages and to quote the docs 可以在react-router-redux github页面中看到详细信息并引用文档

Both push and replace take in a location descriptor, which can be an object describing the URL or a plain string URL. push和replace都包含一个位置描述符,它可以是描述URL的对象或纯字符串URL。 These action creators are also available in one single object as routerActions, which can be used as a convenience when using Redux's bindActionCreators(). 这些动作创建器也可以在一个对象中作为routerActions使用,在使用Redux的bindActionCreators()时可以方便地使用它们。

So use the history.push() inside your mapDispatchToProps directly, or pass them as an prop to be used inside your PostContainer react component. 因此,直接在mapDispatchToProps中使用history.push() ,或者将它们作为要在PostContainer反应组件中使用的prop传递。


A cleaner approach might be to use React Router's <Redirect> : 更简洁的方法可能是使用React Router的<Redirect>

class PostContainer extends React.Component {
  render() {
    if (userJustAddedAPostRightNow === true) {
      return <Redirect to='/posts' />
    }
    return (
      <div>
        <Post onSubmit={addPost}/>
      </div>
    );
  }
}
export default connect(null, { addPost })(PostContainer);

addPost would trigger a change of state if successful. 如果成功, addPost将触发状态更改。 It should change userJustAddedAPostRightNow to true, so that you'll be redirected to the posts' page. 它应该将userJustAddedAPostRightNow更改为true,以便您将被重定向到帖子的页面。

To avoid all those nested callbacks you can follow Nir Kaufman's approach and handle logic in middlewares: 为了避免所有这些嵌套回调,您可以遵循Nir Kaufman的方法并处理中间件中的逻辑:

//src/middlewares/post.js
export const newPost = ({dispatch}) => next => action => {
  next(action);

  if (action.type === ADD_POST) {
    dispatch(apiRequest('POST', action.URL, null, ADD_POST_SUCCESS, ADD_POST_ERROR));
    dispatch(showSpinner());
  }
};

Then, on success, ADD_POST_SUCCESS action would change, via your reducer, the value of userJustAddedAPostRightNow . 然后,成功时, ADD_POST_SUCCESS操作将通过reducer更改userJustAddedAPostRightNow的值。

In my case after history.push() doesn't work directly,在我的情况下, history.push()不能直接工作,

So I found an alternative that, we should reload windows by window.location.reload();所以我找到了一个替代方案,我们应该通过window.location.reload();重新加载窗口window.location.reload();

It work for me!它对我有用!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM