从Redux获取数据时,函数组件的加载屏幕为何未正常显示
你现在的写法从根本上就没把loading的状态和异步请求的执行顺序对齐,直接导致加载屏根本没机会被渲染:
1. setLoading(false) 位置完全错位
梳理你当前useEffect的实际执行顺序:
- 进入effect后,首先定义异步函数
anyNameFunction,这时候函数内部的代码完全不会运行 - 调用
anyNameFunction()时,函数刚走到第一行setLoading(true),就遇到了await dispatch(...)—— 此时异步函数会立刻挂起后续执行流程,给外部返回一个pending状态的Promise,根本不会停下来等请求走完 - 函数挂起后,代码立刻顺次执行effect里剩下的同步语句:也就是你写在外面的
setLoading(false)
从React 18开始,所有同一同步执行上下文里的连续state更新都会被自动批处理合并,不会触发多次独立渲染。你先后执行的setLoading(true)和setLoading(false)同属一个同步任务,最终合并渲染时拿到的loading值直接就是false,loading=true的状态从来没有被单独提交给React渲染管线,加载屏自然不会出现。
至于你能看到等了一会数据才出来的效果,是因为异步请求完成、Redux状态更新触发重渲染时,loading早就被设成了false,直接跳过加载屏渲染了内容区域。
2. 异步等待可能完全失效
如果你写的fetchAddressList是Redux Thunk结构,但没有在最外层返回内部的请求Promise,那么dispatch(fetchAddressList())的返回值是undefined。此时await undefined会立刻进入完成状态,根本等不到接口请求真正结束,哪怕你调整了setLoading的位置,也会出现关loading太快的问题。
把所有loading状态的操作全部收敛到异步函数内部,通过try/finally结构保证状态操作和异步执行顺序完全绑定,删掉异步函数外部的setLoading(false)调用:
useEffect(() => { async function loadAddressList() { setLoading(true); try { // 等待dispatch返回的Promise完全解决 await dispatch(fetchAddressList()); } catch (err) { console.log('地址列表加载失败', err); } finally { // 不管请求成功/失败,走完流程再关闭loading setLoading(false); } } loadAddressList(); }, []);
这里要确保你写的fetchAddressList是标准Thunk结构,一定要在外层return内部的异步逻辑,不然await是拿不到等待结果的:
// 错误写法示例:没有return,dispatch之后返回undefined export const fetchAddressList = () => async (dispatch) => { const res = await axios.get('/address/list'); dispatch({ type: 'SET_ADDRESS_LIST', payload: res.data }); } // 正确写法:有返回Promise,await可以正常等待 export const fetchAddressList = () => { return async (dispatch) => { try { const res = await axios.get('/address/list'); dispatch({ type: 'SET_ADDRESS_LIST', payload: res.data }); } catch (err) { dispatch({ type: 'SET_ADDRESS_ERROR', payload: err }); } } }
如果调整完上述逻辑后加载屏还是显示异常,可以检查布局:你将全屏高度的加载View放在ScrollView内部,如果没有给ScrollView设置contentContainerStyle={{ flexGrow: 1 }},ScrollView的内容会根据子元素总高度拉伸,可能导致初始滚动位置偏移,把加载屏挤到视口外看不到。
内容的提问来源于stack exchange,提问作者Kerry

