设置状态变量后立即执行调度操作

时间:2017-12-23 12:11:20

标签: reactjs redux

我有一个像这样的初始redux状态:

{
  loggedInUserId: null,
  comments: []
}

以下是我的React App的样子:

class App extends Component {
  componentWillMount() {
    this.props.getLoggedInUserId();
  }

  render() {
    return (
      <Switch>
        <Route exact path="/" component={HomePage} />
        <Route path="/comments" component={Comments} />
      </Switch>
    );
  }
}

在我的应用程序中,我发送了一个动作getLoggedInUserId(),它异步填充州内的loggedInUserId

HomePage是一个显示一些文本的愚蠢组件。我启动应用程序(路由现在是&#39; /&#39;),请参阅HomePage组件,然后导航到评论页面,其中包含:

componentWillMount() {
  this.props.fetchComments(this.props.loggedInUserId); // Dispatch action to do API call to fetch user's comments
}

render() {
  // Show this.props.comments nicely formatted
}

一切正常,我在评论组件中看到了评论列表。

但是如果我刷新路由/comments上的页面,那么当评论运行componentWillMount时,尚未加载loggedInUserId,因此它将调用fetchComments(null)

现在,为了解决这个问题,我正在使用我的评论组件:

componentWillMount() {
  if (!this.props.loggedInUserId) return;
  this.props.fetchComments(this.props.loggedInUserId);
}

componentWillReceiveProps(nextProps) {
  if (!this.props.loggedInUserId && nextProps.loggedInUserId) {
    nextProps.fetchComments(nextProps.loggedInUserId);
  }
}

运作良好。但是我在10多个组件中做到这一点,似乎很多工作都可以分解,但我没有找到一种优雅的方法来实现它。

所以我问你如何处理这种情况?欢迎任何想法:

  • HOC
  • 副作用
  • 其他图书馆

4 个答案:

答案 0 :(得分:3)

您可能希望服务器将初始状态呈现为&#39; index.html&#39; (或你有什么)并在客户身上补充水分。

此初始状态包括loggedInUserId页面的/comments 数据。

查看https://redux.js.org/docs/recipes/ServerRendering.html

答案 1 :(得分:3)

我使用Route周围的包装器检查用户是否已登录,如果没有,则将其重定向到登录页面。只有在获取经过身份验证的用户的userId后才会呈现已包装的路由。

import * as React from 'react'
import { Route, Redirect } from 'react-router-dom'
import URLSearchParams from 'url-search-params'

class AuthRoute extends React.Component {
  componentDidMount() {
    if (!this.props.isLoading) {
      this.props.getLoggedInUserId()
    }
  }

  render() {
    if (this.props.isLoading) {
      // first request is fired to fetch authenticated user
      return null // or spinner
    } else if (this.props.isAuthenticated) {
      // user is authenticated
      return <Route {...this.props} />
    } else {
      // invalid user or authentication expired
      // redirect to login page and remember original location
      const search = new URLSearchParams({
        next: this.props.location.pathname,
      })
      const next =
        this.props.location.pathname !== '/' ? `?${search.toString()}` : ''
      return <Redirect to={`/login${next}`} />
    }
  }
}

您需要更新处理getLoggedInUserId操作的reducer,以便同时存储isLoading州。

答案 2 :(得分:2)

我认为在这里使用HOC会很干净。因为所有常见的逻辑将在同一个地方。在这里使用组合 假设你有组件A,B,C,D

现在您想在所有组件的componentWillReceiveProps生命周期中编写一些常用函数。

写一个HOC,如:

class HOC extends React.Component {
componentWillReceiveProps(nextProps) {
//Your commomn logic
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
 child => React.cloneElement(child, {
   ...this.props,
 })
 return (
  <div>
   {childrenWithProps}
  </div>
 )
}
}

按如下方式编写组件:

class A extends React.Component {
componentWillReceiveProps(nextProps) {
  //your uncommone logic
}
render(){
 return (
   <HOC {...this.props}>
    <div>
     //Your page jsx
    </div>
   </HOC>
 )
}
}

为组件B,C和D编写相同的方法。当组件之间存在许多共同点时,此模式非常有用。所以最好看看你的用例

答案 3 :(得分:0)

OP写作。在这里阅读好的想法之后,我决定选择一个定制的HOC:

import React, { Component } from 'react';

const requireProp = (As, propsSelector, propsToDispatch) =>
  class Wrapper extends Component {
    componentWillMount() {
      if (!propsSelector(this.props) && typeof propsToDispatch === 'function') {
        propsToDispatch(this.props);
      }
    }

    render() {
      const { ...props } = this.props;
      return !!propsSelector(this.props) && <As {...props} />;
    }
  };

export default requireProp;

要了解我如何使用它,请参阅this gist