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

时间:2021-06-21 10:41:33

标签: reactjs redux react-redux store dispatch

我遇到了一个奇怪的问题。我调用 dispatch 方法从服务器加载一些数据。这很好用,我设法获取我的数据并更新减速器状态。但是redux store state好像没有更新。 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} alt="" />
                </div>
                <div>
                    <img src={logo} alt="" 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')
);

1 个答案:

答案 0 :(得分: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]));
}