2016-04-22 139 views
3

在传统模式中,您有一个职位列表/posts和详细视图/posts/1。如果您在后端有一台API服务器,并且前端有React + Redux,那么当您到达/posts时,您可能会获取资源。但是当你到达/posts/1时你会怎么做?如果你登陆/posts/第一,你已经拥有所有的资源,所以你也许可以做到以下几点:从API服务器请求资源

  1. posts减速返回所有我们从API服务器获取的帖子
  2. currentPost减速只有一个返回相关的职位

要设置currentPost,你可以为你在索引视图中单击一个post尽快派遣一个更新currentPost动作。

但是,如果你登陆/posts/1而不去索引页,或刷新/posts/1,你没有,你的索引页(即posts减速回报[])加载资源。要解决这个问题,您可以从API服务器请求/posts/1,然后设置currentPost

问题:我是否正确理解流程?我不确定是否需要currentPost减速机。此外,我不确定是否习惯于使用索引页面中的资源并仅在必要时请求单个资源。

+2

对我来说这听起来很合理。对'/ posts'的调用可能无法检索到需要在'/ posts/1'中显示的所有数据,因此您必须获取剩余的内容。在这种情况下,两个减速器绝对有用。 – azium

回答

1

如果您在/posts请求中获得了需要显示currentPost的所有数据,则只需一个减速器即可避免重复项目。

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帖子数据将从服务器获取时,您可以将它传递给您的演示组件。

// 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); 
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个操作:

  • 一个请求的所有帖子(可能包括分页PARAMS)
  • 一个请求单后
// 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> 
    ) 
    } 
    } 
) 

Tada!简单的情况下处理。现在,如果我们想显示一个帖子,我们从商店中读取它,或者获取它。

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 /> 
     } 
    } 
    } 
) 
相关问题