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

Posted

技术标签:

【中文标题】Redux & React Router:结合调度和导航(history.push)【英文标题】:Redux & React Router: Combing dispatch and navigation (history.push) 【发布时间】:2018-06-06 01:44:08 【问题描述】:

对于如何将 React Router 的 history.push('/route')Redux 一起使用,我有点不知所措。

也就是说,如果你用 Redux 的 mapDispatchToProps 连接一个组件,你怎么把应该在 dispatch 某个 action 后触发的 history.push('/route') 放在?

为了澄清这个问题,这是我的实验的 sn-p...

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

如您所见,只要通过addPostFulfilled 将帖子添加到Redux 的状态,页面就需要通过history.push 移动到/posts

根据 Redux 的文档,容器用于更新状态(以及获取数据)。所以,愚蠢的组件不应该放像history.push这样的东西。

有什么比上面的代码更好的方法?

我们将不胜感激。

【问题讨论】:

【参考方案1】:

您可以使用 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);

或者只是将推送作为“完成回调”传递。

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

【讨论】:

非常感谢您的建议。那么,在mapDispatchToProps 中包含除 Redux 操作之外的其他内容是否可以接受?你的代码运行得非常好,但是在 mapDispatchToProps 中看到 done('/posts') 仍然让我感到有些尴尬,因为 React Router 的历史本身与 Dispatch 无关(我没有使用 react-router-redux)。我的意思是,mapDispatchToProps 似乎在处理 Redux 的操作,而不是其他任何事情。我是不是想多了?【参考方案2】:

更简洁的方法可能是使用 React Router 的 &lt;Redirect&gt;:

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 如果成功,将触发状态更改。它应该将userJustAddedAPostRightNow 更改为 true,以便您将被重定向到帖子页面。

为避免所有这些嵌套回调,您可以关注 Nir Kaufman's approach 并在中间件中处理逻辑:

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

然后,如果成功,ADD_POST_SUCCESS 操作将通过您的 reducer 更改 userJustAddedAPostRightNow 的值。

【讨论】:

【参考方案3】:

恕我直言,您甚至可以从您的愚蠢组件中执行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());
            );
        ,      
    

完成addPostFulFilled 数据后,您可以选择在组件本身中执行this.history.push('/route')。您可以使用props 更新时调用的任何生命周期方法来执行此操作。 也许,将其添加到componentWillReceiveProps

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

请注意,上述方法仅在以下情况下有效

    您可以不通过store跟踪位置变化。(Read More) 您正在使用react-router-4,在这种情况下,您将使用withRouter 包装您的连接,这将使历史对象作为道具出现在PostContainer 中。 应注意防止不必要的重新渲染,并在shouldComponentUpdate 中写入必要的逻辑。

如果上面的内容听起来像是一堆废话,您可以随时使用react-router-reduxpush API,它可以在浏览器历史记录中推送新条目。您可以在您选择的任何地方导入它,然后推送一条新路线。

详情可在react-router-redux github pages 中查看并引用文档

push 和 replace 都包含一个位置描述符,它可以是一个 描述 URL 的对象或纯字符串 URL。这些动作创造者 也可以在单个对象中作为 routerActions 使用,它可以是 在使用 Redux 的 bindActionCreators() 时方便使用。

因此,请直接在 mapDispatchToProps 中使用 history.push(),或者将它们作为道具传递给 PostContainer 反应组件。


【讨论】:

【参考方案4】:

在我的情况下,history.push() 不能直接工作,

所以我找到了一个替代方法,我们应该通过window.location.reload();重新加载窗口

它对我有用!

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于Redux & React Router:结合调度和导航(history.push)的主要内容,如果未能解决你的问题,请参考以下文章

菜鸟webpack/react/redux/react-router/ts一步步搭建架子

使用 React-Router 和 Redux 时单击链接时如何调度操作?

路由/重定向不在 React Router 4 和 Redux 授权中呈现

React-Router-Redux:在“react-router-redux”中找不到导出“syncHistoryWithStore”

混淆 react-router-dom 和 react-router-redux

Redux 使用 react-router-redux 连接“块”导航