【问题标题】:Using redux, how to avoid rendering old state data before making a new API call?使用redux,如何避免在进行新的API调用之前渲染旧的状态数据?
【发布时间】:2020-04-22 19:58:56
【问题描述】:

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

注意:我正在使用 Hooks,所以我使用来自 react-reduxuseSelector()useDispatch() 自定义挂钩。

想象一下,我有一些状态可以根据slug 从数据库中获取blogPost

我的 state.blogPost:

{
  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,所以蛞蝓来自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 工作正常(因为此时状态完全为空)。

现在想象一下,我回到主页点击另一篇博文。

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

因此,在我的useEffect 运行之前,我看到屏幕上显示了该信息的闪烁。当它运行时,loading 将被 GET_BLOGPOST_START 操作设置为 true,我会看到加载。

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

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

  • 我需要 本地 loading 状态吗?

  • 当我的组件卸载时,我是否应该分派一个操作将loading 转换为true?所以当我的组件再次为下一个slug 挂载时,它已经是true

  • 我是否应该使用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;

【问题讨论】:

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


    【解决方案1】:

    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]);
    

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

    case "CLEAR_BLOGPOST":
      return initialState;
    

    【讨论】:

    • 谢谢!这是一种常见的做法吗?还是人们通常使用这种“缓存”行为来避免额外的获取?我可以保留以前获取的 blogPost 的列表,并在获取之前检查我的 slug 是否已经有一个。你怎么看?
    • 当状态可能很大或不可重用时,清理是一种常见的做法。当您认为人们会多次访问内容时,缓存是一种常见的做法。如果你想缓存博文,你应该将每篇博文保存在不同的键下(可能是 slug)。但是,由于您从服务器加载它,因此 Web 浏览器缓存可能对您有用,您需要自己管理缓存。
    • 感谢您的详细解释!
    • @cbdeveloper 我认为这必须被接受为答案。
    • @SuleymanSah 我真的很喜欢这个答案,我同意。但是因为我在周末问了这个问题,所以我等了一会儿,看看是否有人对此有不同的方法。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2019-08-04
    • 2020-03-04
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    相关资源
    最近更新 更多