一旦 Redux 状态更新,如何触发 action creator
How to fire an action creator once a Redux state is updated
我正在构建一个 React 组件,用于列出用户当天发布的所有帖子。我有两个 action creators 和 reducer:
fetchUserList()
是一个动作创建者,它获取所有用户的列表并被 reducer 状态捕获 this.props.userList
fetchPosts(userId)
是一个动作创建者,它接受 userId
作为参数并获取今天发布的帖子。这是被 reducer state 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 尝试渲染这个组件时,它会抱怨:
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)。
我正在构建一个 React 组件,用于列出用户当天发布的所有帖子。我有两个 action creators 和 reducer:
fetchUserList()
是一个动作创建者,它获取所有用户的列表并被 reducer 状态捕获this.props.userList
fetchPosts(userId)
是一个动作创建者,它接受userId
作为参数并获取今天发布的帖子。这是被 reducer statethis.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 尝试渲染这个组件时,它会抱怨:
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 tocomponentWillMount
.
我尝试使用其他 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)。