更新Redux状态后如何触发动作创建者

时间:2018-04-24 05:07:12

标签: reactjs redux redux-thunk

我正在构建一个React组件,列出用户当天发布的所有帖子。我有两个动作创建者和缩减者:

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

    该组件看起来像这样:

    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尝试渲染此组件时,它会抱怨:

      

    警告:在现有状态转换期间(例如在render或其他组件的构造函数中)无法更新。渲染方法应该是道具和状态的纯函数;构造函数副作用是反模式,但可以移动到componentWillMount

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

    采用这种模式的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

首先让我们试着了解为什么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内链接这个,我们可以有一个专门的行动创造者为我们做这件事。我希望这能让您更好地了解这些库和中间件的强大功能。我的代码片段只是一个简单的例子,可以对您的代码/操作进行一些假设,并跳过所有&#34; pending&#34;和&#34;错误&#34;的状态。

我建议不要使用componentWillMount,因为将来有理由将其弃用(请参阅docs herethis blogpost)。