使用redux,如何避免在进行新的API调用之前渲染旧的状态数据?
Posted
技术标签:
【中文标题】使用redux,如何避免在进行新的API调用之前渲染旧的状态数据?【英文标题】:Using redux, how to avoid rendering old state data before making a new API call? 【发布时间】:2020-04-22 19:58:56 【问题描述】:我刚刚开始用redux
和react-redux
做一些实验。
注意:我正在使用 Hooks,所以我使用来自 react-redux
的 useSelector()
和 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
呈现时,我的状态中已经存在一些东西(blogPost
或 error
,上次为上一个 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;
【问题讨论】:
【参考方案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 处理程序应该从状态中删除 error
和 blogpost
条目:
case "CLEAR_BLOGPOST":
return initialState;
【讨论】:
谢谢!这是一种常见的做法吗?还是人们通常使用这种“缓存”行为来避免额外的获取?我可以保留以前获取的 blogPost 的列表,并在获取之前检查我的 slug 是否已经有一个。你怎么看? 当状态可能很大或不可重用时,清理是一种常见的做法。当您认为人们会多次访问内容时,缓存是一种常见的做法。如果你想缓存博文,你应该将每篇博文保存在不同的键下(可能是 slug)。但是,由于您从服务器加载它,因此 Web 浏览器缓存可能对您有用,您需要自己管理缓存。 感谢您的详细解释! @cbdeveloper 我认为这必须被接受为答案。 @SuleymanSah 我真的很喜欢这个答案,我同意。但是因为我在周末问了这个问题,所以我等了一会儿,看看是否有人对此有不同的方法。谢谢!以上是关于使用redux,如何避免在进行新的API调用之前渲染旧的状态数据?的主要内容,如果未能解决你的问题,请参考以下文章
在使用 redux-observables 开始另一个史诗之前,如何等待不同的史诗完成并更新商店?
Redux - 我应该从状态或 api 调用中获取产品详细信息吗?