从API服务器

时间:2016-04-22 17:56:35

标签: reactjs redux

在一个非常传统的模式中,您有/posts的帖子列表和/posts/1的详细信息视图。如果后端有API服务器,前端有React + Redux,则可能在到达/posts时获取资源。但是当你到达/posts/1时你会怎么做?如果您首先登陆/posts/,那么您已拥有所有资源,因此您可以执行以下操作:

  1. posts reducer返回我们从API服务器提取的所有帖子
  2. currentPost reducer只返回相关帖子
  3. 要设置currentPost,您可以在索引视图中点击currentPost后立即发送更新post的操作。

    但是,如果您在没有进入索引页面的情况下登陆/posts/1或刷新/posts/1,那么您就没有在索引页面中加载的资源(即posts reducer返回[])。要解决此问题,您可以从API服务器请求/posts/1,然后设置currentPost

    问题:我是否正确理解了流量?我不确定是否需要currentPost减速器。此外,我不确定使用索引页面中的资源是否是常规的,只有在必要时才会请求单个资源。

2 个答案:

答案 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 />
      }
    }
  }
)