简体   繁体   English

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

[英]How to properly get when the redux dispatch is done, to then render a Component

I use react 18.2.0, redux 4.2.0 and redux thunk我使用反应 18.2.0、redux 4.2.0 和 redux thunk

I want to wait until my dispatch is done, so I can take the data it got from the server and render a component with that data我想等到分派完成,这样我就可以从服务器获取数据并使用该数据渲染组件

I try to do the following in my component:我尝试在我的组件中执行以下操作:

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} />);
  } 
})

The issue is that in my console I see:问题是在我的控制台中我看到:

  • BINGO SUBSCRIBE a multiple times BINGO SUBSCRIBE a
  • and then the DATA from store that is inside the store然后是DATA from store
  • and then the BINGO THEN a null idle that is in the then after calling dispatch然后BINGO THEN a null idle在调用 dispatch then

I think that the log of the store should be first我认为商店的日志应该是第一位的

Why I see "BINGO SUBSCRIBE a" that is inside the store.subscribe multiple times?为什么我多次看到store.subscribe中的“BINGO SUBSCRIBE a”?

Why I never see the final data that came from the server?为什么我看不到来自服务器的最终数据? "BINGO THEN a null idle" contains the initial state, even though "DATA from store" brought back data. “BINGO THEN a null idle”包含最初的 state,尽管“DATA from store”带回了数据。

By the way, this my store, for features顺便说一句,这是我的商店,功能

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

I just want to get the data after the dispatch is done, to render a component with them.我只想在分派完成后获取数据,用它们渲染一个组件。 Please help me understand what I am doing wrong and how can I fix that.请帮助我了解我做错了什么以及我该如何解决。

You don't need to do the store.subscribe thing.你不需要做store.subscribe的事情。

The selector is supposed to update and it will cause a render of your component.选择器应该更新,它会导致组件的渲染。 So, initially featureInfoA is undefined but after the promise is resolved it will update the store which will trigger a render in your component因此,最初 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>

Why I see "BINGO SUBSCRIBE a" that is inside the store.subscribe multiple times?为什么我多次看到 store.subscribe 中的“BINGO SUBSCRIBE a”?

That is because, as the docs mention this那是因为,正如文档中提到的那样

Adds a change listener.添加更改侦听器。 It will be called any time an action is dispatched, and some part of the state tree may potentially have changed.它会在任何时候调度一个动作时被调用,并且 state 树的某些部分可能已经改变。

So basically each time any action is dispatched and any part of the state tree might have been changed it will be triggered.所以基本上每次调度任何动作并且 state 树的任何部分可能已被更改时它都会被触发。 This refers to your entire store (not just featuresSlice )这是指您的整个商店(不仅仅是featuresSlice


Why I never see the final data that came from the server?为什么我看不到来自服务器的最终数据? "BINGO THEN a null idle" contains the initial state, even though "DATA from store" brought back data. “BINGO THEN a null idle”包含最初的 state,尽管“DATA from store”带回了数据。

This is most likely because your fetchFeatureInfoById is not returning anything (please note that the return data; part is inside a callback, but the main function doesn't return anything) so in your extra reducer action.payload has no value.这很可能是因为您的fetchFeatureInfoById没有返回任何东西(请注意return data;部分在回调中,但主要的 function 没有返回任何东西)所以在您的额外减速器中action.payload没有价值。

So what you want to do in order to be able to properly set the slice state from the slice extra reducer is returning the fetch result from the fetchFeatureInfoById function. So basically your function would look something like:因此,为了能够从 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: Without a reproducible example is not 100% this will fix all your problems, but it should at the very least point you in the right direction. PS:没有可重现的例子就不是 100% 这会解决你所有的问题,但它至少应该为你指明正确的方向。


Extra suggestion (related to the other answer):额外建议(与其他答案相关):

I think you can at least use an useEffect?我认为您至少可以使用 useEffect? It would be way more efficient to only trigger the an effect when accordionDetailsRootGlobal changes or featureInfoA (so basically have an useEffect with this 2 dependencies and render the ContextInfo when one of this 2 change, rather than rendering on every store update).仅在accordionDetailsRootGlobal更改或featureInfoA时触发效果会更有效(因此基本上有一个具有这 2 个依赖项的 useEffect 并在这 2 个之一更改时呈现ContextInfo ,而不是在每次商店更新时呈现)。 Something like:就像是:

  useEffect(() => {
    if (featureInfoA)
      accordionDetailsRootGlobal.render(<ContextInfo featureData={featureInfoA} />)
  }, [accordionDetailsRootGlobal, featureInfoA])

I Think this might be a better approach to deal with dispatch when you want to observe your data after you request successfully handled我认为当您想在请求成功处理后观察数据时,这可能是处理分派的更好方法

   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