Redux 存储在调度后不更新(异步数据加载)

Posted

技术标签:

【中文标题】Redux 存储在调度后不更新(异步数据加载)【英文标题】:Redux store not updating after dispatch (async data load) 【发布时间】:2021-09-05 00:57:10 【问题描述】:

我遇到了一个奇怪的问题。我调用dispatch 方法从服务器加载一些数据。这很好用,我设法获取我的数据并更新减速器状态。但是 redux 存储状态似乎没有更新。 useSelector 常量永远不会更新。任何想法?感谢您的帮助!

这里是代码...

App.tsx

function App(): ReactElement 

    //* Init *//
    const alertPopupRef = useRef(null);
    const [dataLoaded, setDataLoaded] = useState<boolean>(false);

    const applicationPreferences = useSelector<RootState, ApplicationPreferences>(state => state.applicationPreferences);

    const dispatch = useDispatch();

    useEffect(() => 
        // This code is only triggered once, when the component is mounted.
        console.log(applicationPreferences);
    , [applicationPreferences]);

    dispatch(loadApplicationPreferences(showError));

    // Just for test.
    setTimeout(() => 
        // The 'ApplicationPreferences' variable is still empty. 
        console.log(store.getState());
    , 3000);


    //* Component Functions *//
    function showError(err: ApiBusinessLayerError): void 
        alertPopupRef.current.show(AlertsTypes.Error, err.error, err.description, true);
        console.log("showAlert is triggered.");
    

    return (
        dataLoaded ?
            <AlertContext.Provider value= showError >
                <Alert ref=alertPopupRef />
                <Router>
                    <Switch>
                        <Route exact path="/" component=public_welcome_page />
                        <Route path="/signup" component=public_signup_page />
                    </Switch>
                </Router>
            </AlertContext.Provider>
            :
            <div style=styles.loading_page>
                <Alert ref=alertPopupRef />
                <div style=styles.loading_image_container>
                    <img src=loadingImage  />
                </div>
                <div>
                    <img src=logo  style=styles.logo />
                </div>

                <div>
                    Language: applicationPreferences.language
                </div>

            </div>
    );

application_preferences_reducer.ts

import produce from "immer";
import  ApiBusinessLayerError, ApiDataTypes, get  from "../api_business_layer";
import ApplicationPreferences from "../models/application_preferences";
import  RootState  from "../store";

//* ┌──────────────────┐
//* │ ETI DECLARATIONS │
//* └──────────────────┘
export interface ApplicationPreferencesState 
    language: string;
    server: string;
    isInDevelopement: boolean;


export enum ApplicationPreferencesActionsTypes 
    SET_PREFERENCES = "SET_PREFERENCES"


export type ApplicationPreferencesAction = 
    type: ApplicationPreferencesActionsTypes,
    payload: ApplicationPreferences[]


//* ┌─────────┐
//* │ ACTIONS │
//* └─────────┘
export function loadApplicationPreferences(errorCallback: (err: ApiBusinessLayerError) => void): Function 
    return async (dispatch: (action: ApplicationPreferencesAction) => RootState, getState: () => RootState): Promise<void> => 
        get<ApplicationPreferences>(ApiDataTypes.ApplicationPreferences, null, null)
            .then(data =>  dispatch( type: ApplicationPreferencesActionsTypes.SET_PREFERENCES, payload: data ); )
            .catch((err: ApiBusinessLayerError) =>  errorCallback(err); );
    


//* ┌─────────┐
//* │ REDUCER │
//* └─────────┘
const initialState: ApplicationPreferencesState = 
    language: "",
    server: "",
    isInDevelopement: false


const routes:  [actionType: string]: (state: ApplicationPreferencesState, action: ApplicationPreferencesAction) => ApplicationPreferencesState  = 
    [ApplicationPreferencesActionsTypes.SET_PREFERENCES]: _setApplicationPreferences,


function applicationPreferencesReducer(state: ApplicationPreferencesState = initialState, action: ApplicationPreferencesAction): ApplicationPreferencesState 
    if (!routes[action.type])  return state; 
    return routes[action.type](state, action);


function _setApplicationPreferences(state: ApplicationPreferencesState, action: ApplicationPreferencesAction): ApplicationPreferencesState 
    return produce(state, state => 
        // The state is correctly set here.
        state = JSON.parse(JSON.stringify(action.payload[0]));
    );


export default applicationPreferencesReducer;

store.ts

import  applyMiddleware, CombinedState, combineReducers, createStore  from "redux";
import thunk from "redux-thunk";
import applicationPreferencesReducer,  ApplicationPreferencesState  from "./reducers/application_preferences_reducer";
import languagesReducer,  LanguagesState  from "./reducers/languages_reducer";
import signInReducer,  SignInState  from "./reducers/signin_reducer";
import userPrivilegesReducer,  UserPrivilegesState  from "./reducers/user_privileges_reducer";
import userTypesReducer,  UserTypesState  from "./reducers/user_types_reducer";

export type RootState = CombinedState<
    applicationPreferences: ApplicationPreferencesState;
    language: LanguagesState;
    signIn: SignInState;
    userTypes: UserTypesState;
    userPrivileges: UserPrivilegesState;
>

export const rootReducer = combineReducers(
    applicationPreferences: applicationPreferencesReducer,
    language: languagesReducer,
    signIn: signInReducer,
    userTypes: userTypesReducer,
    userPrivileges: userPrivilegesReducer
);

const store = createStore(rootReducer, applyMiddleware(thunk));

export default store;

index.tsx

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.min.js";
import App from "./App";
import  Provider  from 'react-redux';
import store from "./data/store";

ReactDOM.render(
    <Provider store=store>
        <App />
    </Provider>
    , document.getElementById('root')
);

【问题讨论】:

你是在 index.js 中初始化存储吗?另外,你的应用组件周围有 吗? 是的,我做到了。我将使用 index.tsx 代码更新问题。 【参考方案1】:

不确定为什么要在 reducer 中使用生产,因为您实际上并没有更改任何状态子集,而是返回一个全新创建的状态。我认为您可以执行以下操作:

//don't you hate code that wanders off the right side of the screen so you can't see what's going on, when someone sets their editor/formatter to a line length over 80
function _setApplicationPreferences(state: ApplicationPreferencesState, action: ApplicationPreferencesAction): ApplicationPreferencesState 
    return JSON.parse(JSON.stringify(action.payload[0]));

【讨论】:

太棒了!我现在在工作。我仍然不明白为什么,但它有效!非常感谢!

以上是关于Redux 存储在调度后不更新(异步数据加载)的主要内容,如果未能解决你的问题,请参考以下文章

Redux - 异步 - 输入字段更改时的 POST 状态

Redux thunk:等待异步函数调度

Next js 应用刷新时,useEffect 不调度 redux saga 动作并更新状态

调度操作后 Redux 状态未更新

如何在页面加载时调度 redux 操作以在 useEffect 中加载数据

如何一个接一个地调度两个异步 redux 动作?