使用redux,如何避免在进行新的API调用之前呈现旧状态数据?

时间:2020-01-04 15:59:08

标签: javascript reactjs redux react-redux react-hooks

我刚刚开始使用reduxreact-redux做一些实验。

注意:我使用的是挂钩,因此我使用的是useSelector()中的useDispatch()react-redux自定义挂钩。

想象一下,我有某种状态可以基于blogPost从数据库中获取slug

我的状态。博客帖子:

{
  loading: false,      // TRUE while fetching
  error: null,         // TO keep network errors
  blogPost: null       // TO keep the blogPost
}

BlogPostContainer.js

我正在useEffect内调度动作和异步调用。

注意1::我正在手动执行异步作业,因为我还没有使用redux-thunk

注意2::我正在使用react-router-dom,所以这些sl来自props.match.params.slug

示例:myApp.net/blog/some-blog-post-slug


// ...

const { loading, error, blogPost } = useSelector(state => state.blogPost);
const dispatch = useDispatch();

useEffect(() => {

  dispatch({ type: "GET_BLOGPOST_START", payload: props.match.params.slug });  // START ACTION

  mockAPI(props.match.params.slug)

  .then((result) => {
    dispatch({ type: "GET_BLOGPOST_SUCCESS", payload: result });               // SUCCESS ACTION
  })

  .catch((err) => {
    dispatch({ type: "GET_BLOGPOST_FAIL", payload: err });                     // FAIL ACTION
  })

}, [dispatch,props.match.params.slug]);

// RETURN STATEMENT

return (
  loading ?
    <div>Loading...</div>
  : error ?
    <ErrorPage/>
  : <BlogPostPage blogPost={blogPost}/>
);

问题

这对于第一个加载的blogPost正常工作(因为此时状态完全为空)。

现在想象一下,我回到首页并单击另一个BlogPost。

从第二次开始,当我的BlogPostContainer呈现时,我的状态已经存在(blogPosterror上次为上一个BlogPost子弹加载的状态)。

因此,在我的useEffect运行之前,我在屏幕上看到了闪烁的信息。当它运行时,loading动作将GET_BLOGPOST_START设置为true,我将看到加载情况。

我想立即查看加载页面。不会闪烁任何旧状态数据。

人们在使用redux时通常如何处理?

  • 我需要一个本地 loading状态吗?

  • 在卸下组件时,我应该调度一个将loading转换为true的操作吗?因此,当我的组件再次安装到下一个true时,它已经是slug了吗?

  • 我应该使用useLayoutEffect,以便效果在渲染之前运行,并且看不到闪烁吗? (它可以工作,但感觉不对)。

  • 针对这种情况的最佳解决方案是什么?


额外

我的减速机

const initialState = {
  loading: false,
  error: null,
  blogPost: null
};

function getBlogPostReducer(state = initialState, action) {
  switch (action.type) {
    case "GET_BLOGPOST_START":        // SET LOADING TO TRUE
      return {
        ...initialState,
        loading: true
      };
    case "GET_BLOGPOST_SUCCESS":      // SET BLOGPOST and LOADING TO FALSE
      return {
        ...initialState,
        loading: false,
        blogPost: action.payload
      };
    case "GET_BLOGPOST_FAIL":         // SET ERROR and LOADING TO FALSE
      return {
        ...initialState,
        loading: false,
        error: action.payload
      };
    default:
      return state;
  }
}

export default getBlogPostReducer;

1 个答案:

答案 0 :(得分:3)

useEffect中返回一个清理函数,当组件卸载时,该函数将分派清晰的博客文章操作:

useEffect(() => {
  dispatch({ type: "GET_BLOGPOST_START", payload: props.match.params.slug });  // START ACTION

  mockAPI(props.match.params.slug)

  .then((result) => {
    dispatch({ type: "GET_BLOGPOST_SUCCESS", payload: result });               // SUCCESS ACTION
  })

  .catch((err) => {
    dispatch({ type: "GET_BLOGPOST_FAIL", payload: err });                     // FAIL ACTION
  })

  return () => dispatch({ type: "CLEAR_BLOGPOST" });

}, [dispatch,props.match.params.slug]);

reduce处理程序应该从状态中同时删除errorblogpost条目:

case "CLEAR_BLOGPOST":
  return initialState;