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

cbd*_*per 4 javascript reactjs redux react-redux react-hooks

我刚刚开始用redux和做一些实验react-redux

注:我使用钩子,所以我使用useSelector()useDispatch()从定制挂钩react-redux

想象一下,我有一些状态可以blogPost基于slug.

我的 state.blogPost:

{
  loading: false,      // TRUE while fetching
  error: null,         // TO keep network errors
  blogPost: null       // TO keep the blogPost
}
Run Code Online (Sandbox Code Playgroud)

BlogPostContainer.js

我在一个useEffect.

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

注 2:我正在使用,react-router-dom所以 slugs 来自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}/>
);

Run Code Online (Sandbox Code Playgroud)

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

现在想象一下,我回到主页点击另一个 blogPost。

从第二次开始,当我的BlogPostContainer渲染时,我的状态中已经存在一些东西(ablogPosterror上次为上一次 blogPost slug 加载的 an)。

而作为这样的结果,我看到前右屏幕上显示的信息闪烁我的useEffect奔跑。当它运行时,loading将被操作设置为 true GET_BLOGPOST_START,我会看到加载。

我想立即看到加载页面。没有任何闪烁的旧状态数据。

人们在使用 redux 时通常如何处理这个问题?

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

  • 我应该派遣一个动作转向loadingtrue时候我的组件卸除?因此,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;
Run Code Online (Sandbox Code Playgroud)

Ori*_*ori 7

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]);
Run Code Online (Sandbox Code Playgroud)

并且 reducer 处理程序应该从状态中删除errorblogpost条目:

case "CLEAR_BLOGPOST":
  return initialState;
Run Code Online (Sandbox Code Playgroud)