一旦 Redux 状态更新,如何触发 action creator

How to fire an action creator once a Redux state is updated

我正在构建一个 React 组件,用于列出用户当天发布的所有帖子。我有两个 action creators 和 reducer:

  1. fetchUserList() 是一个动作创建者,它获取所有用户的列表并被 reducer 状态捕获 this.props.userList
  2. fetchPosts(userId) 是一个动作创建者,它接受 userId 作为参数并获取今天发布的帖子。这是被 reducer state this.props.postsList
  3. 捕获的

组件看起来像这样:

class PostList extends Component {
  componentDidMount() {
    this.props.fetchUserList();
  }

  renderPosts = () => {
    this.props.userList.forEach(user => {
      this.props.fetchUsers(user._id);
    });
    this.setState({
      renderedPosts: true
    });
  };

  render() {
    return (
      <div>
        {this.props.userList.length > 0 && !this.state.renderedPosts ? this.renderPosts() : ""}
      </div>
    );
  }

当 React 尝试渲染这个组件时,它会抱怨:

Warning: Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.

我尝试使用其他 React 生命周期方法,例如 ComponentWillUpdate(),并从那里调用操作而不是 render() 方法,但无济于事。

处理这种模式的最佳方法是什么?

首先让我们试着理解为什么 React 抱怨。您的代码试图做的是在组件呈现时更新状态。但是更新状态会导致重新渲染。所以你可能会陷入死循环:

render -> update state -> render -> update state -> ...

因此 setState 不应在渲染期间调用。让我们试着看看我们如何才能做到这一点。我们想首先获取用户,然后为每个用户获取该用户今天发布的帖子。当您将 async 操作与 redux-thunk 一起使用时,理论上您可以像处理任何 Promise 一样连接这些调用。您可以在文档中找到几个示例。让我们玩得开心:

// In your action creators
function fetchUserList() {
  return function(dispatch) {
    return fetch('http://my-api/user-list').then(
      userList => {
        dispatch({
          type: 'FETCH_USER_LIST',
          userList: userList,
        });
        return Promise.resolve(userList);
      }
    );
  };
}

function fetchPosts(userId) {
  return function(dispatch) {
    return fetch(`http://my-api/users/${userId}/posts`).then(
      posts => {
        dispatch({
          type: 'FETCH_USER_POSTS',
          userId: userId,
          posts: posts,
        });
        return Promise.resolve(posts);
      }
    );
  };
}

// Now in your component
class PostList extends Component {
  componentDidMount() {
    const { fetchUserList, fetchPosts } = this.props;

    fetchUserList()
      .then(users => {
        const postsRequests = users.map(user => fetchPosts(user.id));

        return Promise.all(postsRequests);
      })
      .then(() => this.setState({ renderedPosts: true }));
  }

  renderPost(post) {
    // You can add more complete render logic here
    return <p>{post}</p>;
  }

  render() {
    const { postsList, userList } = this.props;
    const { renderedPosts } = this.state;

    return (
      <div>
        {userList.length > 0 && !renderedPosts
           ? postsList.map(post => renderPost(post))
           : ""
        }
      </div>
    );
  }

有几种方法可以实现这一点,查看文档 here 应该很有启发性!它显示了几种组合和组合较小功能以构建您自己的流程的方法。与其从 componentDidMount 中链接它,我们本可以有一个专门的动作创建者来为我们做这件事。我希望这能让您更好地了解这些库和中间件的强大功能。我的代码片段只是一个简单的示例,它对您的 code/actions 做出一些假设并跳过所有 "pending" 和 "error" 状态。

我建议不要使用 componentWillMount,因为它在未来会被弃用(参见 docs here and this blogpost)。