reactjs 如何正确获取redux分派完成的时间,然后呈现组件

9udxz4iz  于 2023-01-12  发布在  React
关注(0)|答案(2)|浏览(127)

bounty将在4天后过期。回答此问题可获得+500声望奖励。slevin希望引起更多人对此问题的关注:请帮助一个react新手了解发生了什么

我使用react 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
  • 然后调用调度后then中的BINGO THEN a null idle

我觉得店的日志应该放在第一位
为什么我多次看到store.subscribe内的“BINGO SUBSCRIBE a”?
为什么我从来没有看到来自服务器的最终数据?“BINGO THEN a null idle”包含初始状态,即使“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

我只想在调度完成后得到数据,用它们来呈现一个组件。请帮助我了解我做错了什么,以及如何修复。

ztyzrc3y

ztyzrc3y1#

你不需要做store.subscribe的事情。
选择器应该更新,并且它将导致组件的呈现。因此,最初featureInfoA是未定义的,但是在承诺解决之后,它将更新存储,这将触发组件中的呈现

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>
ctehm74n

ctehm74n2#

为什么我看到"宾果SUBSCRIBE a"是在商店里。订阅多次?
这是因为正如医生所说
添加更改侦听器。每当调度操作时都会调用该侦听器,并且状态树的某些部分可能已更改。
因此,基本上,每次调度任何操作,并且状态树的任何部分可能已被更改,它都会被触发。这指的是整个存储(不仅仅是featuresSlice
为什么我从来没有看到来自服务器的最终数据?"BINGO THEN a null idle"包含初始状态,即使"DATA from store"返回了数据。
这很可能是因为fetchFeatureInfoById没有返回任何东西(请注意,return data;部分在回调函数中,但main函数没有返回任何东西),所以在额外的reducer中action.payload没有值。
因此,为了能够正确地设置切片额外缩减器的切片状态,您需要做的是从fetchFeatureInfoById函数返回获取结果,因此,基本上您的函数看起来如下所示:

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时触发一个效果会更有效(所以基本上有一个useEffect与这2个依赖项,并在这2个改变之一时渲染ContextInfo,而不是在每次商店更新时渲染)。

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

相关问题