在一个非常传统的模式中,您有/posts
的帖子列表和/posts/1
的详细信息视图。如果后端有API服务器,前端有React + Redux,则可能在到达/posts
时获取资源。但是当你到达/posts/1
时你会怎么做?如果您首先登陆/posts/
,那么您已拥有所有资源,因此您可以执行以下操作:
posts
reducer返回我们从API服务器提取的所有帖子currentPost
reducer只返回相关帖子要设置currentPost
,您可以在索引视图中点击currentPost
后立即发送更新post
的操作。
但是,如果您在没有进入索引页面的情况下登陆/posts/1
或刷新/posts/1
,那么您就没有在索引页面中加载的资源(即posts
reducer返回[]
)。要解决此问题,您可以从API服务器请求/posts/1
,然后设置currentPost
。
问题:我是否正确理解了流量?我不确定是否需要currentPost
减速器。此外,我不确定使用索引页面中的资源是否是常规的,只有在必要时才会请求单个资源。
答案 0 :(得分:1)
如果您获得了在currentPost
请求中显示/posts
所需的所有数据,则只需一个减速器即可避免重复项目。
在postsReducer
中,您需要处理两项操作:
1.当您从服务器获得所有帖子时,您的减速器应该返回它们
2.当您收到特定帖子时,只需将他附加到所有帖子列表并返回结果数组。
//reducers.js
function postsReducer(state = [], action) {
switch (action.type) {
case 'RECEIVE_POSTS':
return action.posts;
case 'RECEIVE_POST':
return [...state, action.post]
default:
return state;
}
}
PostContainer
应该发送一个动作以获取currentPost
。当从服务器获取currentPost发布数据时,您可以将其传递给Post
表示组件。
// PostContainer.js
class PostContainer extends Component {
componentWillMount() {
if (!this.props.post) {
this.props.dispatch(loadPost(this.props.params.id));
};
}
render() {
return (
<Post post={this.props.post}
);
}
}
function mapStateToProps(state, ownProps) {
// if you're using react-router, post id will be in `params` property.
const id = ownProps.params.id;
return {
post: state.posts.filter(post => post.id === id)[0]
};
};
export default connect(mapStateToProps)(PostContainer);
PostsListContainer
应调度操作以从服务器获取所有帖子。请求成功完成后,您会将包含帖子的数组传递给PostsList
组件。
// PostsListContainer.js
class PostsListContainer extends Component {
componentWillMount() {
if (!this.props.posts) {
this.props.dispatch(loadPosts());
}
}
render() {
return (
<PostsList posts={this.props.posts}
);
}
}
function mapStateToProps(state) {
return {
posts: state.posts
}
};
export default connect(mapStateToProps)(PostsListContainer);
答案 1 :(得分:0)
实用的方法是存储所有帖子并请求丢失的帖子。假设你的posts
减速器是这样的:
function posts(state = {}, action) {
switch (action.type) {
case "FETCH_ALL_POSTS":
return {...state, ...action.posts}
case "FETCH_POST":
return {...state, [action.post.id]: action.post}
default:
return state
}
}
您可以定义2个操作:
// Fetch all posts.
//
// In this example we are expecting the response to be like:
//
// {
// 12: {id: 12, title: "Hello", content: "..."},
// 16: {id: 16, title: "Bonjour", content: "..."},
// 54: {id: 54, title: "Hola", content: "..."},
// ...
// }
//
// If you want to return an array instead of a map the you need
// to normalize `posts`.
//
function fetchAllPosts() {
return dispatch => {
fetch("/api/posts")
.then(res => res.json())
.then(posts => dispatch({type: "FETCH_ALL_POSTS", posts}))
}
}
// Fetch a single post.
//
// Response would be:
//
// {id: 12, title: "Hello", content: "..."}
//
function fetchPost(id) {
return (dispatch, getState) => {
const state = getState()
// Check if the post is cached
if (state.posts[id]) {
dispatch({type: "FETCH_POST", post: state.posts[id]})
}
// Otherwise we must query the API
fetch(`/api/post/${id}`)
.then(res => res.json())
.then(post => dispatch({type: "FETCH_POST", post}))
}
}
然后在您的组件中(在安装它们之前或在路由之后),您可以调用上述操作来触发加载。我们考虑您要显示帖子列表:
const PostList = connect(
state => ({
// Retrieve all posts as an array
posts: Object.values(state.posts),
}),
dispatch => ({
fetchAllPosts: () => dispatch(fetchAllPosts()),
})
)(
class PostList extends Component {
componentWillMount() {
// Load all posts if none were stored
if (this.props.posts.length === 0) {
this.props.fetchAllPosts()
}
}
render() {
return (
<ul>
{this.props.posts.map(
post => <PostItem key={post.id} id={post.id} />
)}
</ul>
)
}
}
)
const PostItem = connect(
(_, initialProps) => {
return state => ({
// Get the post data
post: state.posts[initialProps.id],
})
}
)(
class PostItem extends Component {
render() {
return (
<li>{this.props.post.title}</li>
)
}
}
)
多田!处理简单的案例。现在,如果我们想要显示一个帖子,我们会从商店中读取它,或者获取它。
const PostDetails = connect(
(_, initialProps) => {
// Read the post ID from the initial properties.
// We could imagine another case where the ID is read from the location.
const {id} = initialProps
return state => {
// May, or may not, return a post
post: state.posts[id],
}
},
(dispatch, initialProps) => {
// Same as above, we need to retrieve the post ID.
const {id} = initialProps
// Prepare an action creator to load THIS post.
const fetchThisPost = () => {
dispatch(fetchPost(id))
}
return () => ({
fetchThisPost,
})
}
)(
class PostDetails extends Component {
componentWillMount() {
// Load this post if it is not cached
if (!this.props.post) {
this.props.fetchThisPost()
}
}
render() {
if (!this.props.post) {
return <Loading />
} else {
return <PostCard />
}
}
}
)