how to initialise all states when press reset button in redux - reactjs

in my app i'm using redux with redux-thunk.there is on RESET Button, when user click it i want INITIALIZE all redux state instead of window.location.reload().
index.js (reducers index file)
import { combineReducers } from 'redux';
import { reducer as reduxFormReducer } from "redux-form";
import authReducer from './auth_reducer';
import main from './main'
import BookingData from './booking_reducer';
import StepOne from './stepone';
import StepTwo from './stepTwo'
const rootReducer = combineReducers({
form: reduxFormReducer,
auth: authReducer,
Main:main,
StepOneReducer:StepOne,
StepTwoReducer:StepTwo,
BookingData:BookingData,
})
export default rootReducer;
here each value of object contains separate reducer file and each reducer file has number of states
App.js (root file)
import ReactDOM from "react-dom";
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import AppRouter from './routers/AppRouter';
import reducers from './reducers';
const middleware = [
reduxThunk,
];
const store = createStore(reducers, composeWithDevTools(
applyMiddleware(...middleware),
// other store enhancers if any
));
const token = localStorage.getItem('token');
if(token){
store.dispatch({type:AUTH_USER});
}
ReactDOM.render(
<Provider store={store}>
<AppRouter />
</Provider>
, document.getElementById('app'));
so my problem is when press Reset Button it will call action Creator, it will dispatch the action when it comes to the reducer(index reducer) I'm unable reset all state.
i saw this Question also but here i don't want to change my index reducer file structure because I need export both and project logic also get mess.
how can i initialize all redux state when user click Reset Button

If I understand you correctly, then I am doing a similar thing in my apps:
I reset my entire redux state by dispatching a 'reset', or 'initialize' action, and have a case in all reducers for this action, where I set the state to the initial values.

actually i was exporting AppReducer instead of rootReducer
import { combineReducers } from 'redux';
import { reducer as reduxFormReducer } from "redux-form";
import authReducer from './auth_reducer';
import main from './main'
import BookingData from './booking_reducer';
import StepOne from './stepone';
import StepTwo from './stepTwo'
import {RESET} from '../actions/types';
const AppReducer = combineReducers({
form: reduxFormReducer,
auth: authReducer,
Main:main,
StepOneReducer:StepOne,
StepTwoReducer:StepTwo,
BookingData:BookingData,
})
const rootReducer = (state, action) => {
if (action.type === RESET) {
console.log('reset action inside root');
state = undefined
}
return AppReducer(state, action)
}
export default rootReducer;
worked Dan Abramov solution

Related

Why is Redux-Persist not persisting the store

Hi there everyone I was messing around with react(redux to be precise) and came across a problem. When I refresh the page redux store does as well everything returns to its initial value, I googled the problem and found out that I have to use redux-persist. However even that is not working, I think the problem is with how I configured redux-persist but I could be wrong.
The code below is how I went about the redux-persist configuration.
// configureStore.js
import { createStore, applyMiddleware } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "../reducers/index";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
const persistConfig = {
key: "root",
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(
persistedReducer,
composeWithDevTools(applyMiddleware(thunk))
);
export const persistedStore = persistStore(store);
The code below shows how I went about making the rootReducer.
// index.js
import { combineReducers } from "redux";
import { reducer as formReducer } from "redux-form";
import authReducer from "./auth";
import messageReducer from "./message";
import chatroomReducer from "./chatroom";
import { LOGOUT_SUCCESS } from "../actions/authTypes";
const apiReducer = combineReducers({
authReducer,
messageReducer,
chatroomReducer,
form: formReducer,
});
const rootReducer = (state, action) => {
if (action.type === LOGOUT_SUCCESS) {
state = undefined;
}
return apiReducer(state, action);
};
export default rootReducer;
And the code below is the index.js that comes when creating the react app.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { PersistGate } from "redux-persist/integration/react";
import { Provider } from "react-redux";
import { store, persistedStore } from "./Redux/configureStore";
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistedStore}>
<React.StrictMode>
<App />
</React.StrictMode>
</PersistGate>
</Provider>,
document.getElementById("root")
);
reportWebVitals();
Can anyone tell me what is wrong with my code? If you need additional info please let me know.
Try configuring the whitelist, where you must indicate which reducers to store.
Remember to import the reducers before adding them to the list
import navigation from "./reducers/navigation";
// WHITELIST
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['navigation'] // only navigation will be persisted
};
First, you need to import storage from Async
import AsyncStorage from '#react-native-community/async-storage';
Use WhiteList like this
const persistConfig = {
key: 'root',
storage: AsyncStorage,
whitelist: [
'user'
],
};
"user" is the specific root reducer you want to store
As per this answer, you also need to clear out the redux-persist storage when you catch the LOGOUT_SUCCESS action.
First, you need to import the appropriate storage engine and then clear it out completely.
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);
};

Uncaught Could not find router reducer in state tree, it must be mounted under "router"

React router works normal. But if I add <Redirect> or call from <Link>, I have this exception:
Uncaught Could not find router reducer in state tree, it must be mounted under "router"
rootReducer.js:
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import counterReducer from './Counter/counter.reducer';
import sidebarReducer from './Sidebar/sidebar.reducer';
export default (history) => combineReducers({
router: connectRouter(history),
counter: counterReducer,
sidebar: sidebarReducer,
});
store.js:
import { createBrowserHistory } from 'history';
import { applyMiddleware, compose, createStore } from 'redux';
import { routerMiddleware } from 'connected-react-router';
import createRootReducer from './rootReducer';
export const history = createBrowserHistory();
export default function configureStore(preloadedState) {
const composeEnhancer = compose
const store = createStore(
createRootReducer(history),
preloadedState,
composeEnhancer(
applyMiddleware(
routerMiddleware(history),
),
),
);
if (module.hot) {
module.hot.accept('./rootReducer', () => {
store.replaceReducer(createRootReducer(history));
});
}
console.log(store.getState());
return store;
}
Check the history documentation. If you're using react-router-dom#5.x.x then you should use history#4.10.1 because the latest version of history (v5) only works with react-router-dom#6.x.x

Redux-Persist and Connected-React-Router: TypeError: Cannot read property 'location' of undefined

I am trying to get React app to work properly with React Router and Redux, but I constantly getting TypeError: Cannot read property 'location' of undefined to any components that have redux. I am using connected-react-router lib to register router in the store but apparently I did something wrong
store/configureStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from '../reducers';
import { persistStore } from 'redux-persist';
import { createBrowserHistory } from 'history';
import { routerMiddleware } from "connected-react-router";
let _persistor;
let _store;
export const history = createBrowserHistory();
export const getStore = (props) => {
if (_store) {
return _store;
}
const initialState = (props) ? {...props} : {};
_store = createStore(
reducer,
initialState,
compose(
applyMiddleware(routerMiddleware(history)),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
),
);
_persistor = persistStore(_store);
return _store;
};
export const getPersistor = () => {
return _persistor;
};
reducers/index.js
import { combineReducers } from 'redux';
import { persistReducer } from 'redux-persist';
import Menu from './Menu';
import SelectedServices from './SelectedServices';
import { connectRouter } from 'connected-react-router'
import { history } from "../store/configureStore";
const rootReducer = (history) => combineReducers({
router: connectRouter(history),
menu: Menu,
selectedServices: SelectedServices,
});
const reducer = persistReducer(
{
key: 'root',
storage: require('localforage'),
whitelist: [],
},
rootReducer(history),
);
export default reducer;
After three hours of debugging and reading more about each library of connected-react-router, react-redux, and react-router-dom I found one line which I skip several times, but eventually I checked it in my package.json. At current moment:
v6.0.0 requires React v16.4.0 and React Redux v6.0. (It does not support React Redux v7.0 yet. We're looking into the compability issue.)
I was using react-redux: v7.0.1 which a real facepalm to my self. So I downgraded to v6 and everything start works fine.

How to get store from redux-persist on app load in react-native?

I have a problem running react-native app. I've got a store that uses redux-persist and some other middleware and the problem is when the app loads, data from the store is not there, only the default values. However as soon as I touch any button the data is there, so the problem is I can't delay rendering until the store is rehydrated. I've tried options with wrapping the app with PersistGate with both loading and bootstrapped values, however this does not work for me. The current code is:
Store config:
import { createStore, applyMiddleware, compose } from 'redux';
import promise from 'redux-promise-middleware';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import { AsyncStorage } from 'react-native';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import rootReducer from './reducers';
const middleware = [
promise(),
thunk,
];
if (__DEV__) { // eslint-disable-line
// middlewares.push();
}
const enhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // eslint-disable-line
const persistConfig = {
key: 'root',
storage: AsyncStorage,
stateReconciler: autoMergeLevel2,
};
const pReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(pReducer, {}, enhancers(applyMiddleware(...middleware)));
export const persistor = persistStore(store);
Root Reducer:
import { combineReducers } from 'redux';
import loginScreenReducer from 'screens/login/LoginScreen.reducer';
export default combineReducers({
loginScreenReducer,
});
App:
import { AppLoading } from 'expo';
import React from 'react';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import Root from 'Root.component';
import { store, persistor } from 'reduxConfig/store';
export default class App extends React.Component {
state = { }
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
{(bootstrapped) => {
if (bootstrapped) {
console.log(store.getState());
return <Root />;
}
return <AppLoading />;
}}
</PersistGate>
</Provider>
);
}
}
The component I'd like to make work correctly:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Navigator from './Navigator';
import { LoginScreen } from '../screens';
#connect(
state => ({
token: state.loginScreenReducer.token,
isLogged: state.loginScreenReducer.isLogged,
}),
)
export default class AppNavigator extends Component {
state = { }
render() {
const {
token,
} = this.props;
if (token) {
return <Navigator />;
}
return <LoginScreen />;
}
}
export const { router } = Navigator;
The component shows login screen first but if I touch any button on it, it renders Navigator component. Also when I get the state of the store (App, before returning <Root />, it has the next structure, containing empty object, which is currently default for loginScreenReducer:
The app works correctly except for redux-persist.
===== New Answer =====
The old answer is wrong, the problem came back later. It looks like it's a problem with redux-persist, or even expo, react-native-scripts or whatever, I was not been able to locate the problem source. The behavior I described before comes from redux-persist inability to access AsyncStorage. However the only thing that helps now is completely stopping expo and running it again, after that redux-persist is able to access AsyncStorage again.
It also looks like disabling live reload really helps with this problem if not stops it at all.
===== Old Answer =====
I've ended up by downgrading react-redux package to version 4.4.9, looks like this has solved the problem, otherwise the code I posted initially should be totally okay. The problem with auto-filling state on button press was caused my a couple of my own mistakes both from BE and FE sides, that lead to store be filled with different items. They are not in the given code. Also I've changed my store a little, I'm not sure if the initial problem was connected to enhancers combining. So now my store looks like this:
import { createStore, applyMiddleware } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import { AsyncStorage } from 'react-native';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise-middleware';
import { createLogger } from 'redux-logger';
import { LOGOUT } from 'screens/login/LoginScreen.actions';
import appReducer from './reducers';
const middlewares = [
promiseMiddleware(),
thunk,
];
if (__DEV__) { // eslint-disable-line
middlewares.push(createLogger());
}
const rootReducer = (state, action) => {
if (action.type === LOGOUT) {
state = undefined;
}
return appReducer(state, action);
};
const persistConfig = {
key: 'root',
storage: AsyncStorage,
stateReconciler: autoMergeLevel2,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(persistedReducer, undefined, applyMiddleware(...middlewares));
export const persistor = persistStore(store);
I hope this will help somebody.

combineReducers causes code to break

This is subsequent to the thread I posted here
After lot of troubleshooting I found that this code works without any problems
import React from 'react';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import thunkMiddleware from 'redux-thunk';
import { Provider } from 'react-redux';
import DataTableReducer from './reducers/DataTableReducer';
import DimensionPickerReducer from './reducers/DimensionPickerReducer';
const loggerMiddleware = createLogger();
const store = createStore(
DimensionPickerReducer,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
);
export default store;
But if I replace my single reducer with a combine reducer call like
import React from 'react';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import thunkMiddleware from 'redux-thunk';
import { Provider } from 'react-redux';
import DataTableReducer from './reducers/DataTableReducer';
import DimensionPickerReducer from './reducers/DimensionPickerReducer';
const loggerMiddleware = createLogger();
const store = createStore(
combineReducers({
DataTableReducer,
DimensionPickerReducer
}),
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
);
export default store;
I immediately start getting errors by the DimensionPicker control that the mandatory props were not specified.
So the combineReducer method is not working for me.
I am uploaded a sample project here which shows the problem.
https://github.com/abhitechdojo/MovieLensReact
You will have to run npm install after doing a git clone
With combined reducers your store will have data structure like that:
{
DimensionPickerReducer: {
dimenisionName: '',
pickerIsLoading: false,
pickerError: '',
currentAttribute: '',
attributeList: []
},
DataTableReducer: {
tableData: [],
tableIsLoading:false,
tableError: ''
}
}
So you should adjust your containers to work with combined store. For example in DimensionPickerContainer.js you should change mapStateToProps function:
const mapStateToProps = (state) => {
return {
attributeList : state.DimensionPickerReducer.attributeList,
currentAttribute : state.DimensionPickerReducer.currentAttribute
}
}
You can also name your reduces in store, so they would not look ugly in data structure. E.g. combineReducers({ dimensionPicker: DimensionPickerReducer, dataTable: DataTableReducer})

Resources