useEffect rewrites the entire store - javascript

I have a redux store with 3 reducers:
let reducers = combineReducers({
config: configReducer,
data: dataReducer,
currentState: gameStateRecuder})
let store = createStore(reducers, applyMiddleware(thunkMiddleware));
In each of those reducers the initial store is empty, but once the App component mounts I use useEffect to replace each initial store inside a reducer with the one I receive with axios.get using redux-thunk. It looks like this in every reducer:
let initialState = [];
const SET_STATE = 'SET_STATE';
const configReducer = (state = initialState, action) => {
switch (action.type) {
case SET_STATE: {
return { ...action.state};
}
default:
return state;
}
const setState = (state) => ({ type: SET_STATE, state });
export const getConfigState = () => (dispatch) => {
getAPI.getConfig() //I import getAPI with all the REST API logic//
.then(response => {
dispatch(setState(response));
})
};
And the App trigger is:
const App = (props) => {
useEffect(() => {
props.getConfigState();
props.getDataState();
props.getGameState();
}, []);
return (
//JSX//
);
}
export default compose(connect(null, { getConfigState, getDataState, getGameState }))(App);
However, when the App mounts, I have this mess:
In the end, I get the state of each reducer replaced with the state of the one whose promise resolved the last one. I can try to wrap the app 2 more times with a HOC that does nothing but re-writes a state of the precise reducer, but I would still like to understand what causes a promise to affect other reducers besides the one he needs to effect.

A silly mistake, but maybe someone has the exact same problem - the solution is to give different case names for each reducer - SET_STATE need to become SET_GAME_STATE, SET_CONFIG_STATE, SET_DATA_STATE respectivly. I believe that's because of my misunderstanding on how the dispatch works.

Related

no data is passed into state when using useContext/useReducer together with useQuery

export const itemReducer = (state, action) => {
switch (action.type) {
default:
return state
}
}
import React, { useState, useReducer, createContext, useContext } from 'react'
import { useQuery } from '#apollo/client'
import { CURRENT_MONTH_BY_USER } from '../graphql/queries'
import { itemReducer } from '../reducers/ItemReducer'
const Items = createContext()
export const ItemProvider = ({ children }) => {
let items = []
const [state, dispatch] = useReducer(itemReducer, { items: items })
const result = useQuery(CURRENT_MONTH_BY_USER)
if (result.data && result.data.getCurrentMonthByUser) {
items = [...result.data.getCurrentMonthByUser]
}
return <Items.Provider value={{ state, dispatch }}>{children}</Items.Provider>
}
export const ItemsState = () => {
return useContext(Items)
}
export default ItemProvider
let items gets correct data from the useQuery, however nothing is passed into the state, therefore I am unable to transfer data into another components from the context. What am I doing wrong here?
When debugging both items and state they're initially empty because of the loading however then only the items receives correct data and state remains as empty array.
If i put static data into let items it works just fine, so maybe there can be something wrong with my useQuery as well?
It's easy to see your problem if you look at where items is used. That's only as the initial state to your useReducer call - but items is only set to a non-empty value after this. That has absolutely no effect on the component, because items is not used later in your component function, and the initial state is only ever set once, on the first render.
To solve this you need to embrace your use of a reducer, adding a new action type to set this initial data, and then dispatching that when you have the data. So add something like this to your reducer:
export const itemReducer = (state, action) => {
switch (action.type) {
case SET_INITIAL_DATA: // only a suggestion for the name, and obviously you need to define this as a constant
return { ...state, items: action.items };
/* other actions here */
default:
return state
}
}
and then rewrite your component like this:
export const ItemProvider = ({ children }) => {
const [state, dispatch] = useReducer(itemReducer, { items: [] })
const result = useQuery(CURRENT_MONTH_BY_USER)
if (result.data && result.data.getCurrentMonthByUser) {
dispatch({ type: SET_INITIAL_DATA, items: result.data.getCurrentMonthByUser });
}
return <Items.Provider value={{ state, dispatch }}>{children}</Items.Provider>
}
Also, while this is unrelated to your question, I will note that your ItemsState export appears to be a custom hook (it can't be anything else since it isn't a component but uses a hook) - that is perfectly fine but there is a very strong convention in React that all custom hooks have names of the form useXXX, which I strongly suggest you should follow. So you could rename this something like useItemsState (I would prefer useItemsContext to make clear it's just a useContext hook specialised to your specific context).

Using single Redux reducer to get and store multiple results

I am wanting to use Redux to perform multiple dispatches to the same endpoint to fetch different responses, however I am unsure of how I can best store them within the Redux store...
myAction.js
export const GET_DATA = 'GET_DATA';
export const GET_DATA_SUCCESS = 'GET_DATA_SUCCESS';
export const GET_DATA_ERROR = 'GET_DATA_ERROR';
export const getData = (path) => ({
type: GET_DATA,
agent: agent.getData.get,
agentData: path
});
myReducer.js
export default (state = {}, action) => {
...
switch (action.type) {
...
case GET_DATA_SUCCESS:
newState = action.payload;
break;
...
I will call the API like so:
const mapDispatchToProps = dispatch => {
getData: path => getData(path);
}
...
// Using dispatch
getData('/myDataPath');
agent.js
...
getData : {
get = path => performGet('https://my.url/data' + path);
}
Now I know this works perfectly fine for performing a single GET to the endpoint and storing in Redux store under a single entry.
Is there a way in which I can store multiple responses in the Redux store entry?
You could use a dynamic property using bracket notation in your state based on the agentData or other identifying piece of your action.
It would look something like this:
Reducer
export default (state = {}, action) => {
...
switch (action.type) {
...
case GET_DATA_SUCCESS:
newState = {
...state, // Keep all the other values (agentData entries)
[action.agentData]: action.payload // Add/overwrite the current one
};
break;
...

how to use redux useSelector to check state and do a fetch to a database

I am using redux in a project and I want to make a useSelector that would check to see if the values in the redux state are the default values if not it will do a request to the database and update the the state I feel like it is quite complicated though and I am having a hard time getting my head around how I need to do this.
I need to do this because sometimes the correct state is not loaded in the state I am considering just doing a check every time I use useSelector to check if the values are the default values then fetch from the database but I would much prefer to write it a way that would allow to be handled within the redux selector but I can't really grasp I how I need to do it.
const info = useSelector(getInfo)
Ideally I would like the info to be handled when I fetch here
import { SET_USER_DETAILS } from "../constants/userConstants";
const intialState = {
user: { },
};
const userReducer = (state = intialState, action: any) => {
switch (action.type) {
case SET_USER_DETAILS:
return { ...state, user: action.payload };
default:
return state;
}
};
here is what my current reducer looks like what would be the best way to do this as I am finding it a little bit difficult to follow the documentation on the redux website.
You can use redux-thunk. https://redux.js.org/usage/writing-logic-thunks
then your thunk could look something like that:
const thunkFunction = (dispatch, getState) => {
// logic here that can dispatch actions or read state
const currentState = getState() as typeof initialState;
// check if state is default state
if (JSON.stringify(initialState) === JSON.stringify(currentState)) {
fetch(url).then(data => {
dispatch({type: SET_USER_DETAILS, payload: data})
})
}
}
You need first to fetch data in react component:
const MyComponent = () => {
// while fetch is fetching, data will be default state,
// and when fetch is done, that component will automatically
// rerender with new data
const data = useSelector(getInfo);
const dispatch = useDispatch();
useEffect(() => {
dispatch(thunkFunction)
},[])
return <code>{JSON.stringify(data, null, 2)}</code>
}
I did not test it so may require some changes
but in general concept is like this

React/Redux how to access the state in the networkservice

I have created a Network service component which deals with the API call. I want to retrieve state from other components which update the store.
Im having trouble getting the state so I started using Redux, but I havent used Redux before and still trying to find a way to pass the state to the NetworkService. Any help would be great, thanks!
Here is my NetworkService.js
import RequestService from './RequestService';
import store from '../store';
const BASE_URL = 'api.example.com/';
const REGION_ID = //Trying to find a way to get the state here
// My attempt to get the state, but this unsubscribes and
// doesnt return the value as it is async
let Updated = store.subscribe(() => {
let REGION_ID = store.getState().regionId;
})
class NetworkService {
getForecast48Regional(){
let url =`${BASE_URL}/${REGION_ID }`;
return RequestService.getRequest(url)
}
}
export default new NetworkService();
store.js
import {createStore} from 'redux';
const initialState = {
regionId: 0
};
const reducer = (state = initialState, action) => {
if(action.type === "REGIONAL_ID") {
return {
regionId: action.regionId
};
}
return state;
}
const store = createStore(reducer);
export default store;
My folder heirarchy looks like this:
-App
----Components
----NetworkService
----Store
Do not import store directly. Use thunks/sagas/whatever for these reasons.
NetworkService should not know about anything below.
Thunks know only about NetworkService and plain redux actions.
Components know only about thunks and store (not store itself, but Redux's selectors, mapStateToProps, mapDispatchToProps).
Store knows about plain redux actions only.
Knows - e.g. import's.
//////////// NetworkService.js
const networkCall = (...args) => fetch(...) // say, returns promise
//////////// thunks/core/whatever.js
import { networkCall } from 'NetworkService'
const thunk = (...args) => (dispatch, getState) => {
dispatch(startFetch(...args))
const componentData = args
// I'd suggest using selectors here to pick only required data from store's state
// instead of passing WHOLE state to network layer, since it's a leaking abstraction
const storeData = getState()
networkCall(componentData, storeData)
.then(resp => dispatch(fetchOk(resp)))
.catch(err => dispatch(fetchFail(err)))
}
//////////// Component.js
import { thunk } from 'thunks/core/whatever'
const mapDispatchToProps = {
doSomeFetch: thunk,
}
const Component = ({ doSomeFetch }) =>
<button onClick={doSomeFetch}>Do some fetch</button>
// store.subscribe via `connect` from `react-redux`
const ConnectedComponent = connect(..., mapDispatchToProps)(Component)

How to reset the state of a Redux store?

I am using Redux for state management.
How do I reset the store to its initial state?
For example, let’s say I have two user accounts (u1 and u2).
Imagine the following sequence of events:
User u1 logs into the app and does something, so we cache some data in the store.
User u1 logs out.
User u2 logs into the app without refreshing the browser.
At this point, the cached data will be associated with u1, and I would like to clean it up.
How can I reset the Redux store to its initial state when the first user logs out?
One way to do that would be to write a root reducer in your application.
The root reducer would normally delegate handling the action to the reducer generated by combineReducers(). However, whenever it receives USER_LOGOUT action, it returns the initial state all over again.
For example, if your root reducer looked like this:
const rootReducer = combineReducers({
/* your app’s top-level reducers */
})
You can rename it to appReducer and write a new rootReducer delegating to it:
const appReducer = combineReducers({
/* your app’s top-level reducers */
})
const rootReducer = (state, action) => {
return appReducer(state, action)
}
Now we just need to teach the new rootReducer to return the initial state in response to the USER_LOGOUT action. As we know, reducers are supposed to return the initial state when they are called with undefined as the first argument, no matter the action. Let’s use this fact to conditionally strip the accumulated state as we pass it to appReducer:
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
return appReducer(undefined, action)
}
return appReducer(state, action)
}
Now, whenever USER_LOGOUT fires, all reducers will be initialized anew. They can also return something different than they did initially if they want to because they can check action.type as well.
To reiterate, the full new code looks like this:
const appReducer = combineReducers({
/* your app’s top-level reducers */
})
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
return appReducer(undefined, action)
}
return appReducer(state, action)
}
In case you are using redux-persist, you may also need to clean your storage. Redux-persist keeps a copy of your state in a storage engine, and the state copy will be loaded from there on refresh.
First, you need to import the appropriate storage engine and then, to parse the state before setting it to undefined and clean each storage state key.
const rootReducer = (state, action) => {
if (action.type === SIGNOUT_REQUEST) {
// for all keys defined in your persistConfig(s)
storage.removeItem('persist:root')
// storage.removeItem('persist:otherKey')
return appReducer(undefined, action);
}
return appReducer(state, action);
};
Dan Abramov's answer is correct except we experienced a strange issue when using the react-router-redux package along with this approach.
Our fix was to not set the state to undefined but rather still use the current routing reducer. So I would suggest implementing the solution below if you are using this package
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
const { routing } = state
state = { routing }
}
return appReducer(state, action)
}
Define an action:
const RESET_ACTION = {
type: "RESET"
}
Then in each of your reducers assuming you are using switch or if-else for handling multiple actions through each reducer. I am going to take the case for a switch.
const INITIAL_STATE = {
loggedIn: true
}
const randomReducer = (state=INITIAL_STATE, action) {
switch(action.type) {
case 'SOME_ACTION_TYPE':
//do something with it
case "RESET":
return INITIAL_STATE; //Always return the initial state
default:
return state;
}
}
This way whenever you call RESET action, you reducer will update the store with default state.
Now, for logout you can handle the like below:
const logoutHandler = () => {
store.dispatch(RESET_ACTION)
// Also the custom logic like for the rest of the logout handler
}
Every time a userlogs in, without a browser refresh. Store will always be at default.
store.dispatch(RESET_ACTION) just elaborates the idea. You will most likely have an action creator for the purpose. A much better way will be that you have a LOGOUT_ACTION.
Once you dispatch this LOGOUT_ACTION. A custom middleware can then intercept this action, either with Redux-Saga or Redux-Thunk. Both ways however, you can dispatch another action 'RESET'. This way store logout and reset will happen synchronously and your store will ready for another user login.
Just a simplified answer to Dan Abramov's answer:
const rootReducer = combineReducers({
auth: authReducer,
...formReducers,
routing
});
export default (state, action) =>
rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);
Using Redux Toolkit and/or Typescript:
const appReducer = combineReducers({
/* your app’s top-level reducers */
});
const rootReducer = (
state: ReturnType<typeof appReducer>,
action: AnyAction
) => {
/* if you are using RTK, you can import your action and use it's type property instead of the literal definition of the action */
if (action.type === logout.type) {
return appReducer(undefined, { type: undefined });
}
return appReducer(state, action);
};
From a security perspective, the safest thing to do when logging a user out is to reset all persistent state (e.x. cookies, localStorage, IndexedDB, Web SQL, etc) and do a hard refresh of the page using window.location.reload(). It's possible a sloppy developer accidentally or intentionally stored some sensitive data on window, in the DOM, etc. Blowing away all persistent state and refreshing the browser is the only way to guarantee no information from the previous user is leaked to the next user.
(Of course, as a user on a shared computer you should use "private browsing" mode, close the browser window yourself, use the "clear browsing data" function, etc, but as a developer we can't expect everyone to always be that diligent)
const reducer = (state = initialState, { type, payload }) => {
switch (type) {
case RESET_STORE: {
state = initialState
}
break
}
return state
}
You can also fire an action which is handled by all or some reducers, that you want to reset to initial store. One action can trigger a reset to your whole state, or just a piece of it that seems fit to you. I believe this is the simplest and most controllable way of doing this.
With Redux if have applied the following solution, which assumes I have set an initialState in all my reducers (e.g. { user: { name, email }}). In many components I check on these nested properties, so with this fix, I prevent my renders methods are broken on coupled property conditions (e.g. if state.user.email, which will throw an error user is undefined if the upper mentioned solutions).
const appReducer = combineReducers({
tabs,
user
})
const initialState = appReducer({}, {})
const rootReducer = (state, action) => {
if (action.type === 'LOG_OUT') {
state = initialState
}
return appReducer(state, action)
}
UPDATE NGRX4
If you are migrating to NGRX 4, you may have noticed from the migration guide that the rootreducer method for combining your reducers has been replaced with the ActionReducerMap method. At first, this new way of doing things might make resetting the state a challenge. It is actually straightforward, yet the way of doing this has changed.
This solution is inspired by the meta-reducers API section of the NGRX4 Github docs.
First, lets say your are combining your reducers like this using NGRX's new ActionReducerMap option:
//index.reducer.ts
export const reducers: ActionReducerMap<State> = {
auth: fromAuth.reducer,
layout: fromLayout.reducer,
users: fromUsers.reducer,
networks: fromNetworks.reducer,
routingDisplay: fromRoutingDisplay.reducer,
routing: fromRouting.reducer,
routes: fromRoutes.reducer,
routesFilter: fromRoutesFilter.reducer,
params: fromParams.reducer
}
Now, let's say you want to reset the state from within app.module
//app.module.ts
import { IndexReducer } from './index.reducer';
import { StoreModule, ActionReducer, MetaReducer } from '#ngrx/store';
...
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
return function(state, action) {
switch (action.type) {
case fromAuth.LOGOUT:
console.log("logout action");
state = undefined;
}
return reducer(state, action);
}
}
export const metaReducers: MetaReducer<any>[] = [debug];
#NgModule({
imports: [
...
StoreModule.forRoot(reducers, { metaReducers}),
...
]
})
export class AppModule { }
And that is basically one way to achieve the same affect with NGRX 4.
My workaround when working with typescript, built on top of Dan Abramov's answer (redux typings make it impossible to pass undefined to reducer as the first argument, so I cache initial root state in a constant):
// store
export const store: Store<IStoreState> = createStore(
rootReducer,
storeEnhacer,
)
export const initialRootState = {
...store.getState(),
}
// root reducer
const appReducer = combineReducers<IStoreState>(reducers)
export const rootReducer = (state: IStoreState, action: IAction<any>) => {
if (action.type === "USER_LOGOUT") {
return appReducer(initialRootState, action)
}
return appReducer(state, action)
}
// auth service
class Auth {
...
logout() {
store.dispatch({type: "USER_LOGOUT"})
}
}
Simply have your logout link clear session and refresh the page. No additional code needed for your store. Any time you want to completely reset the state a page refresh is a simple and easily repeatable way to handle it.
If you are using redux-actions, here's a quick workaround using a HOF(Higher Order Function) for handleActions.
import { handleActions } from 'redux-actions';
export function handleActionsEx(reducer, initialState) {
const enhancedReducer = {
...reducer,
RESET: () => initialState
};
return handleActions(enhancedReducer, initialState);
}
And then use handleActionsEx instead of original handleActions to handle reducers.
Dan's answer gives a great idea about this problem, but it didn't work out well for me, because I'm using redux-persist.
When used with redux-persist, simply passing undefined state didn't trigger persisting behavior, so I knew I had to manually remove item from storage (React Native in my case, thus AsyncStorage).
await AsyncStorage.removeItem('persist:root');
or
await persistor.flush(); // or await persistor.purge();
didn't work for me either - they just yelled at me. (e.g., complaining like "Unexpected key _persist ...")
Then I suddenly pondered all I want is just make every individual reducer return their own initial state when RESET action type is encountered. That way, persisting is handled naturally. Obviously without above utility function (handleActionsEx), my code won't look DRY (although it's just a one liner, i.e. RESET: () => initialState), but I couldn't stand it 'cuz I love metaprogramming.
Combining Dan Abramov's answer, Ryan Irilli's answer and Rob Moorman's answer, to account for keeping the router state and initializing everything else in the state tree, I ended up with this:
const rootReducer = (state, action) => appReducer(action.type === LOGOUT ? {
...appReducer({}, {}),
router: state && state.router || {}
} : state, action);
I have created actions to clear state. So when I dispatch a logout action creator I dispatch actions to clear state as well.
User record action
export const clearUserRecord = () => ({
type: CLEAR_USER_RECORD
});
Logout action creator
export const logoutUser = () => {
return dispatch => {
dispatch(requestLogout())
dispatch(receiveLogout())
localStorage.removeItem('auth_token')
dispatch({ type: 'CLEAR_USER_RECORD' })
}
};
Reducer
const userRecords = (state = {isFetching: false,
userRecord: [], message: ''}, action) => {
switch (action.type) {
case REQUEST_USER_RECORD:
return { ...state,
isFetching: true}
case RECEIVE_USER_RECORD:
return { ...state,
isFetching: false,
userRecord: action.user_record}
case USER_RECORD_ERROR:
return { ...state,
isFetching: false,
message: action.message}
case CLEAR_USER_RECORD:
return {...state,
isFetching: false,
message: '',
userRecord: []}
default:
return state
}
};
I am not sure if this is optimal?
My take to keep Redux from referencing to the same variable of the initial state:
// write the default state as a function
const defaultOptionsState = () => ({
option1: '',
option2: 42,
});
const initialState = {
options: defaultOptionsState() // invoke it in your initial state
};
export default (state = initialState, action) => {
switch (action.type) {
case RESET_OPTIONS:
return {
...state,
options: defaultOptionsState() // invoke the default function to reset this part of the state
};
default:
return state;
}
};
I've created a component to give Redux the ability to reset state, you just need to use this component to enhance your store and dispatch a specific action.type to trigger reset. The thought of implementation is the same as what Dan Abramov said in their answer.
Github: https://github.com/wwayne/redux-reset
The following solution worked for me.
I added resetting state function to meta reducers.The key was to use
return reducer(undefined, action);
to set all reducers to initial state. Returning undefined instead was causing errors due to the fact that the structure of the store has been destroyed.
/reducers/index.ts
export function resetState(reducer: ActionReducer<State>): ActionReducer<State> {
return function (state: State, action: Action): State {
switch (action.type) {
case AuthActionTypes.Logout: {
return reducer(undefined, action);
}
default: {
return reducer(state, action);
}
}
};
}
export const metaReducers: MetaReducer<State>[] = [ resetState ];
app.module.ts
import { StoreModule } from '#ngrx/store';
import { metaReducers, reducers } from './reducers';
#NgModule({
imports: [
StoreModule.forRoot(reducers, { metaReducers })
]
})
export class AppModule {}
Dan Abramov's answer helped me solve my case. However, I encountered a case where not the entire state had to be cleared. So I did it this way:
const combinedReducer = combineReducers({
// my reducers
});
const rootReducer = (state, action) => {
if (action.type === RESET_REDUX_STATE) {
// clear everything but keep the stuff we want to be preserved ..
delete state.something;
delete state.anotherThing;
}
return combinedReducer(state, action);
}
export default rootReducer;
Just an extension to #dan-abramov answer, sometimes we may need to retain certain keys from being reset.
const retainKeys = ['appConfig'];
const rootReducer = (state, action) => {
if (action.type === 'LOGOUT_USER_SUCCESS' && state) {
state = !isEmpty(retainKeys) ? pick(state, retainKeys) : undefined;
}
return appReducer(state, action);
};
This approach is very right: Destruct any specific state "NAME" to ignore and keep others.
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
state.NAME = undefined
}
return appReducer(state, action)
}
For me to reset the state to its initial state, I wrote the following code:
const appReducers = (state, action) =>
combineReducers({ reducer1, reducer2, user })(
action.type === "LOGOUT" ? undefined : state,
action
);
I found that Dan Abramov's answer worked well for me, but it triggered the ESLint no-param-reassign error - https://eslint.org/docs/rules/no-param-reassign
Here's how I handled it instead, making sure to create a copy of the state (which is, in my understanding, the Reduxy thing to do...):
import { combineReducers } from "redux"
import { routerReducer } from "react-router-redux"
import ws from "reducers/ws"
import session from "reducers/session"
import app from "reducers/app"
const appReducer = combineReducers({
"routing": routerReducer,
ws,
session,
app
})
export default (state, action) => {
const stateCopy = action.type === "LOGOUT" ? undefined : { ...state }
return appReducer(stateCopy, action)
}
But maybe creating a copy of the state to just pass it into another reducer function that creates a copy of that is a little over-complicated? This doesn't read as nicely, but is more to-the-point:
export default (state, action) => {
return appReducer(action.type === "LOGOUT" ? undefined : state, action)
}
First on initiation of our application the reducer state is fresh and new with default InitialState.
We have to add an action that calls on APP inital load to persists default state.
While logging out of the application we can simple reAssign the default state and reducer will work just as new.
Main APP Container
componentDidMount() {
this.props.persistReducerState();
}
Main APP Reducer
const appReducer = combineReducers({
user: userStatusReducer,
analysis: analysisReducer,
incentives: incentivesReducer
});
let defaultState = null;
export default (state, action) => {
switch (action.type) {
case appActions.ON_APP_LOAD:
defaultState = defaultState || state;
break;
case userLoginActions.USER_LOGOUT:
state = defaultState;
return state;
default:
break;
}
return appReducer(state, action);
};
On Logout calling action for resetting state
function* logoutUser(action) {
try {
const response = yield call(UserLoginService.logout);
yield put(LoginActions.logoutSuccess());
} catch (error) {
toast.error(error.message, {
position: toast.POSITION.TOP_RIGHT
});
}
}
One thing Dan Abramov's answer doesn't do is clear the cache for parameterized selectors. If you have a selector like this:
export const selectCounter1 = (state: State) => state.counter1;
export const selectCounter2 = (state: State) => state.counter2;
export const selectTotal = createSelector(
selectCounter1,
selectCounter2,
(counter1, counter2) => counter1 + counter2
);
Then you would have to release them on logout like this:
selectTotal.release();
Otherwise, the memoized value for the last call of the selector and the values of the last parameters will still be in memory.
Code samples are from the ngrx docs.
A quick and easy option that worked for me was using redux-reset . Which was straightforward and also has some advanced options, for larger apps.
Setup in create store
import reduxReset from 'redux-reset'
// ...
const enHanceCreateStore = compose(
applyMiddleware(...),
reduxReset() // Will use 'RESET' as default action.type to trigger reset
)(createStore)
const store = enHanceCreateStore(reducers)
Dispatch your 'reset' in your logout function
store.dispatch({
type: 'RESET'
})
Approach with Redux Toolkit:
export const createRootReducer = (history: History) => {
const rootReducerFn = combineReducers({
auth: authReducer,
users: usersReducer,
...allOtherReducers,
router: connectRouter(history),
});
return (state: Parameters<typeof rootReducerFn>[0], action: Parameters<typeof rootReducerFn>[1]) =>
rootReducerFn(action.type === appActions.reset.type ? undefined : state, action);
};
why don't you just use return module.exports.default() ;)
export default (state = {pending: false, error: null}, action = {}) => {
switch (action.type) {
case "RESET_POST":
return module.exports.default();
case "SEND_POST_PENDING":
return {...state, pending: true, error: null};
// ....
}
return state;
}
Note: make sure you set action default value to {} and you are ok because you don't want to encounter error when you check action.type inside the switch statement.
Another option is to:
store.dispatch({type: '##redux/INIT'})
'##redux/INIT' is the action type that redux dispatches automatically when you createStore, so assuming your reducers all have a default already, this would get caught by those and start your state off fresh. It might be considered a private implementation detail of redux, though, so buyer beware...
for me what worked the best is to set the initialState instead of state:
const reducer = createReducer(initialState,
on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
...initialState
})),
If you want to reset a single reducer
For example
const initialState = {
isLogged: false
}
//this will be your action
export const resetReducer = () => {
return {
type: "RESET"
}
}
export default (state = initialState, {
type,
payload
}) => {
switch (type) {
//your actions will come her
case "RESET":
return {
...initialState
}
}
}
//and from your frontend
dispatch(resetReducer())

Categories