[英]How to properly get when the redux dispatch is done, to then render a Component
我使用反应 18.2.0、redux 4.2.0 和 redux thunk
我想等到分派完成,这样我就可以从服务器获取数据并使用该数据渲染组件
我尝试在我的组件中执行以下操作:
import store from '../redux/store';
const featureInfoA = useSelector(featureInfo)
const featureErrorA = useSelector(featureError)
const featureStatusA = useSelector(featureStatus)
const clickFeatureFromList = (id) => {
//start dispatching
dispatch(fetchFeatureInfoById({ id, category }))
.then((e) => {
console.log('BINGO THEN ', featureInfoA, featureErrorA, featureStatusA);
})
}
//check store when a specific state changed and then get its data and render a component
store.subscribe(() => {
const state = store.getState()
if (state.features.status == 'succeeded' || state.features.status == 'failed') {
console.log('BINGO SUBSCRIBE ', featureInfoA);
accordionDetailsRootGlobal.render(<ContextInfo featureData={featureInfoA} />);
}
})
问题是在我的控制台中我看到:
BINGO SUBSCRIBE a
DATA from store
BINGO THEN a null idle
在调用 dispatch then
我认为商店的日志应该是第一位的
为什么我多次看到store.subscribe
中的“BINGO SUBSCRIBE a”?
为什么我看不到来自服务器的最终数据? “BINGO THEN a null idle”包含最初的 state,尽管“DATA from store”带回了数据。
顺便说一句,这是我的商店,功能
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
const FEATURE_URL = 'http://localhost:3500/map'
//status : idle, loading, succeeded, failed
const initialState = {
info:'a',
status:'idle',
error:null
}
export const fetchFeatureInfoById = createAsyncThunk('feature/fetchInfoById', (originalData) => {
console.log('fetchFeatureInfoById originalData ', originalData);
fetch(FEATURE_URL + '/feature/' + originalData.id + '/' + originalData.category)
.then((response) => response.json())
.then((data) => {
console.log('DATA from store ', data);
return data;
})
.catch((error) => {
return error.message;
});
})
export const featuresSlice = createSlice({
name: 'features',
initialState,
reducers: {
featureInfoById: {
reducer(state, action) {
//do something with the id
console.log('feature state ', ' -- state : ',state.status, ' -- action : ', action);
},
prepare(category, id) {
return{
payload:{
category,
id
}
}
}
}
},
extraReducers(builder) {
builder
.addCase(fetchFeatureInfoById.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchFeatureInfoById.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
.addCase(fetchFeatureInfoById.fulfilled, (state, action) => {
console.log('fulfilled data store ', state, action);
state.status = 'succeeded'
state.info = action.palyload
})
}
})
export const featureInfo = (state) => state.features.info;
export const featureError = (state) => state.features.error;
export const featureStatus = (state) => state.features.status;
export const {featureInfoById} = featuresSlice.actions
export default featuresSlice.reducer
我只想在分派完成后获取数据,用它们渲染一个组件。 请帮助我了解我做错了什么以及我该如何解决。
你不需要做store.subscribe
的事情。
选择器应该更新,它会导致组件的渲染。 因此,最初 featureInfoA 是未定义的,但在解决 promise 后,它将更新商店,这将触发组件中的渲染
import store from '../redux/store';
const featureInfoA = useSelector(featureInfo)
const featureErrorA = useSelector(featureError)
const featureStatusA = useSelector(featureStatus)
const clickFeatureFromList =(id) => {
//start dispatching
dispatch(fetchFeatureInfoById({id, category}))
}
// add some logic here to check if featureInfoA has value
if(featureErrorA) return <div>Error</div>
if(featureStatusA === "loading") return <div>Loading...</div>
if(featureInfoA) return <div>{featureInfoA}</div>
为什么我多次看到 store.subscribe 中的“BINGO SUBSCRIBE a”?
那是因为,正如文档中提到的那样
添加更改侦听器。 它会在任何时候调度一个动作时被调用,并且 state 树的某些部分可能已经改变。
所以基本上每次调度任何动作并且 state 树的任何部分可能已被更改时它都会被触发。 这是指您的整个商店(不仅仅是featuresSlice
)
为什么我看不到来自服务器的最终数据? “BINGO THEN a null idle”包含最初的 state,尽管“DATA from store”带回了数据。
这很可能是因为您的fetchFeatureInfoById
没有返回任何东西(请注意return data;
部分在回调中,但主要的 function 没有返回任何东西)所以在您的额外减速器中action.payload
没有价值。
因此,为了能够从 slice extra reducer 正确设置 slice state,您需要做的是从fetchFeatureInfoById
function 返回获取结果。所以基本上您的 function 看起来像:
export const fetchFeatureInfoById = createAsyncThunk('feature/fetchInfoById', (originalData) => {
console.log('fetchFeatureInfoById originalData ', originalData);
// Add the return keyword before the fetch from the next line
return fetch(FEATURE_URL + '/feature/' + originalData.id + '/' + originalData.category)
.then((response) => response.json())
.then((data) => {
console.log('DATA from store ', data);
return data;
})
.catch((error) => {
return error.message;
});
})
PS:没有可重现的例子就不是 100% 这会解决你所有的问题,但它至少应该为你指明正确的方向。
额外建议(与其他答案相关):
我认为您至少可以使用 useEffect? 仅在accordionDetailsRootGlobal
更改或featureInfoA
时触发效果会更有效(因此基本上有一个具有这 2 个依赖项的 useEffect 并在这 2 个之一更改时呈现ContextInfo
,而不是在每次商店更新时呈现)。 就像是:
useEffect(() => {
if (featureInfoA)
accordionDetailsRootGlobal.render(<ContextInfo featureData={featureInfoA} />)
}, [accordionDetailsRootGlobal, featureInfoA])
我认为当您想在请求成功处理后观察数据时,这可能是处理分派的更好方法
const clickFeatureFromList =async (id) => {
//start dispatching
const result = await dispatch(fetchFeatureInfoById({ id, category }))
if (fetchFeatureInfoById.fulfilled.match(result)) {
const {payload} = result
console.log('BINGO THEN ', featureInfoA, featureErrorA, featureStatusA);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.