与Redux的异步操作

时间:2016-07-28 15:02:54

标签: javascript reactjs redux

我有一个连接到Redux商店的React组件。我在componentWillMount生命周期方法中获取资源(帖子)。

componentWillMount() {
  this.props.fetchPosts();
}

该组件将订阅Redux商店并从商店获取isFetchingposts

const mapStateToProps = (state) => {
  return {
    posts: getAllPosts(state),
    isFetching: getIsFetchingPosts(state),
  }
}

我希望在它仍在抓取时显示一个微调器,所以在render方法中我想这样做。

render() {
   if (this.props.isFetching) {
     return <Spinner />
   }

   return this.props.posts.map(post => <PostItem key={post.id}{...post}/>)
}

但如果我在isFetching方法中使用console.log render,则首先显示false然后显示true,然后最后显示false

理想情况下,此容器第一次呈现时isFetching状态已设置为true并显示微调器。为实现这一目标,我需要做出哪些改变?

以下是动作创建者和减少者的代码

/*** Action Creator ***/
export const fetchPosts = () => (dispatch) => {
  dispatch({
    type: REQUEST_POSTS,
  });

  return axios({
    method: 'get',
    url: `${API_URL}/posts`,
  })
  .then(({data}) => {
    dispatch({
      type: RECEIVE_POSTS,
      payload: data.posts,
    })
  })
  .catch((response) => {
    // some error handling.
  });
}


/*** Reducers ***/
const initialState = {
  isFetching: false,
  allIds: [],
  byId: {},
};

const isFetching = (state = initialState.isFetcthing, action) => {
  switch (action.type) {
    case REQUEST_POSTS:
      return true;
    case RECEIVE_POSTS:
      return false;
    default:
      return state;
  }
}

const allIds = (state = initialState.allIds, action) => {
  switch (action.type) {
    case RECEIVE_POSTS:
      return action.payload.map(post => post.id);
    default:
      return state;
  }
}

const byId = (state = initialState.byId, action) => {
  switch (action.type) {
    case RECEIVE_POSTS:
      return action.payload.reduce((nextState, post) => {
        nextState[post.id] = post;
        return nextState;
      }, {...state});
    default:
      return state;
  }
}

const posts = combineReducers({
  isFetching,
  allIds,
  byId,
});

export default posts;


/*** Selectors in 'posts.js' file ***/

export const getAllPosts = (state) => {
  const { allId, byId } = state;
  return allIds.map(id => byId[id]);
}

/*** rootReducer file ***/
import posts, * as fromPosts from './posts';


const rootReducer = combineReducers({
  posts,
})

export default rootReducer;

export const getAllPosts = (state) => {
  return fromPosts.getAllPosts(state.posts);
};

提前谢谢!

1 个答案:

答案 0 :(得分:1)

明确的答案是,鉴于您的实施,这是预期的行为。您将isFetching状态映射到道具。以下是发生的事情:

  1. 状态树中isFetching的初始值为false,因此isFetching prop值为false,因此呈现为false }。

  2. 您发送的操作会在状态树中将isFetching更改为true。此新状态将映射到新isFetching道具值true,这会导致重新渲染,其呈现为true

  3. 您(异步)调度另一个操作,将状态树中的isFetching更改回false。与(2)中的相同,这会导致重新呈现,isFetchingfalse

  4. 如果您只想使用当前实现的true, false *渲染,那么简单的解决方案就是在缩减器的初始状态中将isFetching设置为true。< / p>

    这个实现在这个组件的设计级别上是否有意义是一个更广泛的问题,在这里没有足够的上下文回答: - )

    完整性

    *更新我应该说我不知道​​render()函数是否会被调用两次,isFetching被解析为true,false或者在这种情况下使用true,true,false三次。我怀疑react-redux可以优化组件的渲染,这样如果映射的isFetching道具从true - > true改变,则不会重新渲染,但是不要我肯定知道这一点 - 如果你能告诉我你的日志记录输出会不会感激和感兴趣?

    在任何情况下,由于标准的反应虚拟DOM差异优化,在DOM级别肯定只会发生两次渲染,所以实际上结果是相同的