I have a Redux store configured and I'm trying to access the state from outside of a React component. I'm using store.getState() but it's returning the initial state values for everything in the store even though the store is populated with the correct data (visible in the UI and dev tools). It just seems to be that when the store is imported and the state is accessed via getState() that the initial values are returned instead of what is actually in the store. Code below.
store.ts
import * as _ from "@reduxjs/toolkit/node_modules/redux-thunk";
import { Action, configureStore } from "@reduxjs/toolkit";
import { ThunkAction } from "redux-thunk";
import { useDispatch } from "react-redux";
import reducers from "./reducers";
const store = configureStore({
reducer: reducers,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}),
});
export type AppDispatch = typeof store.dispatch;
export type IRootState = ReturnType<typeof store.getState>;
export type AppThunk = ThunkAction<void, IRootState, unknown, Action<string>>;
// Export a hook that can be reused to resolve types
export const useAppDispatch = () => useDispatch<AppDispatch>();
export default store;
Service that I'm calling the store from:
import store from "store";
import { LoggingService } from "logger";
const logData(data) {
const user = store.getState().user;
LoggingService.log(user.email, data);
}
In the example above, the store.getState().user returns an object, but the email is null. However, the email is actually populated as it can be seen in the UI and in dev tools. Any help would be much appreciated.
Also to note, this is not an SSR app.
The store.getState() only returns current state from redux store once. Subsequent changes to the state via dispatch action will not call this method again unless use subscribe(listener).
Instead of using store.subscribe directly, we can write a custom observable store utility. Why? see issue#303
index.js:
export function toObservable(store) {
return {
subscribe({ onNext }) {
let dispose = store.subscribe(() => onNext(store.getState()));
onNext(store.getState());
return { dispose };
},
};
}
How to use it? Let's write a test to explain this:
import { combineReducers, createStore } from 'redux';
import { toObservable } from './';
const LoggingService = {
log: console.log,
};
describe('toObservable', () => {
test('should pass', () => {
function userReducer(state = { email: '' }, action) {
switch (action.type) {
case 'GET_USER_FULFILLED':
return { ...state, ...action.payload };
default:
return state;
}
}
const store = createStore(combineReducers({ user: userReducer }));
// create store observable somewhere, may be in the application initialization phase
const store$ = toObservable(store);
store$.subscribe({ onNext: (state) => LoggingService.log(state.user.email) });
// Later, dispatch action in component
store.dispatch({ type: 'GET_USER_FULFILLED', payload: { email: 'example@gmail.com' } });
});
});
Test result:
PASS redux-toolkit-example packages/redux-toolkit-example/examples/observer-store/index.test.ts
toObservable
✓ should pass (23 ms)
console.log
at onNext (packages/redux-toolkit-example/examples/observer-store/index.test.ts:22:58)
console.log
example@gmail.com
at onNext (packages/redux-toolkit-example/examples/observer-store/index.test.ts:22:58)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.937 s
As you can see, every time the state changes, the onNext method will be called.
store.getState() does not update immediately. You have to use useSelector hook in Functional Component in order to get the updated state from the redux. like:
const user = useSelector((state) => state.user);