【问题标题】:How to fire an action creator once a Redux state is updatedRedux 状态更新后如何触发动作创建者
【发布时间】:2018-10-04 06:00:03
【问题描述】:

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

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

组件看起来像这样:

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 或其他组件的构造函数中)。渲染方法应该是 props 和 state 的纯函数;构造函数副作用是一种反模式,但可以移至componentWillMount

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

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

【问题讨论】:

  • this.props.fetchUserList.forEach 这有效吗?不应该是this.props.userList.forEach
  • 您是否尝试移至componentWillMount()
  • @Prajwal 抱歉在复制粘贴代码时出现拼写错误
  • 为什么不去掉 renderPost 状态,只用一个纯组件来渲染帖子呢?这样它只会在更改时重新渲染。还可以尝试 fetchTodaysPostForUsers(userid_list) 操作,这样状态只会在所有结果可用后更新,我假设 fetch 在这里是异步的。

标签: reactjs redux redux-thunk


【解决方案1】:

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

render -&gt; update state -&gt; render -&gt; update state -&gt; ...

因此,在渲染过程中永远不应该调用setState。让我们尝试看看我们如何才能完成这项工作。我们想首先获取用户,然后为每个用户获取该用户今天发布的帖子。当您使用 asyncredux-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 中链接它,我们可以有一个专门的动作创建器来为我们做这件事。我希望这能让您更好地了解这些库和中间件的强大功能。我的代码 sn-p 只是一个简单的示例,它对您的代码/操作做出一些假设并跳过所有“待处理”和“错误”状态。

我建议不要使用componentWillMount,因为它会在未来被弃用,原因很充分(请参阅docs herethis blogpost)。

【讨论】:

  • 感谢您的回答,我需要花一些时间进一步研究它。我目前正在使用 async/await 语法 - 我假设我仍然可以使用它来连接您的示例中的调用。
  • 当然没问题,花一些时间尝试完全掌握这些模式是值得的,因为您会经常使用它们。将其转换为 async/await 语法应该很容易。让我知道这是怎么回事;)
  • 我正在努力用 async/await 语法来复制它——你有一些代码可以和我分享吗?
猜你喜欢
  • 2017-01-24
  • 2012-07-31
  • 2016-06-10
  • 2020-11-09
  • 2019-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-07
相关资源
最近更新 更多