2016-04-22 138 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 /> 
     } 
    } 
    } 
) 
相關問題