我正在构建一个React组件,列出用户当天发布的所有帖子。我有两个动作创建者和缩减者:
fetchUserList()
是一个动作创建者,可以获取所有用户的列表并被reducer状态this.props.userList
fetchPosts(userId)
是一个动作创建者,接受userId
作为参数并获取今天发布的帖子。这是由reducer状态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
或其他组件的构造函数中)无法更新。渲染方法应该是道具和状态的纯函数;构造函数副作用是反模式,但可以移动到componentWillMount
。
我尝试使用其他React生命周期方法,例如ComponentWillUpdate()
,并从那里调用操作而不是render()
方法,但无济于事。
采用这种模式的最佳方法是什么?
答案 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 here和this blogpost)。