繁体   English   中英

如何在 redux 调度完成时正确获取,然后渲染组件

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM