I want to save data of every reducer in localStorage in the form of key value pairs
UserReducer: {isLoding: true, ....}
OrderReducer : {isRecieved: false, ....}
So if there is any changes happen in any of the reducer I want to set the updated data in local storage.
What I am thinking is I can do this store.subscribe but how I could know that which reducer has been changed so I can set the whole reducer data to localStorage.
Need help. Thanks :)
One way to do it is to create a lastAction reducer which will store the value of the last dispatched action. Then in store.subscribe you could do:
let nextState = store.getState();
let dispatchedAction = nextState.lastAction.type;
And when you know the last dispatched action, you can update the local storage depending on that action type.
You can create a redux middleware, it will get fired everytime an action is dispatched. Within the middleware you can put a conditional check to see if prev and next store are same. If not, you can update your local storage.
I found this helpful for creating a middleware. And it's fairly easy.
I think you could not know the changes from the store.subscribe.
The last place when you know the changes are the actions.
You can put the persisting logic to the reducers. But maybe you should not want to introduce side effect in your reducer.
So basically you have two options if you rule out the first one.
Persist the whole store as a single object to local storage. It is the easier option. I would try this solution first and I would measure the overhead with a worst case scenario.
Make a redux middleware and capture the individual actions. You can copy the reducer pattern to persist only the object that changed.
You can use subscribe for option 2. You can use middleware for option 2 and option 3.
I came up with one more solution.
You can do the persisting in your action creator with Redux Thunk or with Redux Saga.
You can use React Component for that. Connect it to the Store, and use it to write to localStorage instead of rendering something.
Imho, it would be easier to write than middleware.
You can use redux-watch to listen to the changes in redux store
for e.g.
// ... other imports/requires
import watch from 'redux-watch'
// assuming you have an admin reducer / state slice
console.log(store.getState().admin.name) // 'JP'
// store is THE redux store
let w = watch(store.getState, 'admin.name')
store.subscribe(w((newVal, oldVal, objectPath) => {
console.log('%s changed from %s to %s', objectPath, oldVal, newVal)
// admin.name changed from JP to JOE
}))
// somewhere else, admin reducer handles ADMIN_UPDATE
store.dispatch({ type: 'ADMIN_UPDATE', payload: { name: 'JOE' }})
this may help you.
I want to extend the approach 3 that #PeterAmbruzs shared. Actually the problem which I faced was whenever there was an action dispatched, store wasn't updated immediately but I still wanted to persist it so I imported reducer and passed state and action into it to get the required next state
import reducer from './reducer'
const someMiddleware = store => next => action => {
const state = store.getState(),
const nextState = reducer(state, action)
// Save it to Local Storage
// Or Make an API Call to save it to server
next(action)
}
Related
I know mutating state can work against PureComponent (or similar)
Is there other reason not to mutate state?
I wonder if the 3rd way is ok to do?
// The Right Way:
// copy the existing items and add a new one
addItemImmutably = () => {
this.setState({
items: [...this.state.items, this.makeItem()]
});
};
// The Wrong Way:
// mutate items and set it back
addItemMutably = () => {
this.state.items.push(this.makeItem());
this.setState({ items: this.state.items });
};
// is this ok? (mutate but set state with new copy)
addItem3rdWay = () => {
this.state.items.push(this.makeItem());
this.setState({items: [...this.state.items]});
}
Here is an example: You have fired an async method that sends a request with your current state data. In meantime you have executed a function that mutates the state. This will cause the async function to send the mutated state despite the fact it intended to send the state before mutation.
You can think state as your database.
You don't directly mutate your database, you modify your database by API.
setState is the API.
Of course, you can directly mutate your database, but other components will have a hard time retrieving those data, because those are inconsistent right now, because somebody doesn't use the API that framework provides for you.
If you really like the way mutating state, you can use Vue, Vue is designed like that.
I'm an absolute beginner in react-redux and I watched many videos and articles,docs and couldn't understand how the state flows between reducers and between store to reducer.
I am having state for each reducer like this
const initState = {todos:[]}
cont reducer = (state=initState,action) ....
Reducer 2 similarly with different state
initState2 = {todo:" "}
reducer2 = (state=initState2,action) ...
And then I import and combine the reducers. Here I am using two different reducers which have different states and is it the right way of doing things ? If so, How can redux be called single state if each reducer have its own individual state.
Don't we have a single state in the store which is accessed by all the reducers and dispatch actions to change state of store directly instead of changing the reducer's store. Any help is appreciated and it may seems like a silly question but filling the gaps is really important and many beginners have same doubt as mine and please do help. Thank you
You only need 1 reducer to store your todos.
How can redux be called single state if each reducer have its own
individual state.
The application effectively has only 1 global store where all the application state is stored. What the reducer returns is what effectively gets stored in the store.
The configuration of what is stored is a map (key-value) where the key is defined in the root reducer, and the value is what is returned from the reducer function.
The way you have to look at it is that the view is "dumb", in that the only thing it does is tell the app what it wants by dispatching an action. This action is just an event that is marked with some string you give it to identify clearly what it is the view wants. The reducer intercepts this action and updates the state in the store accordingly. This state in turn is accessible to all the components in your app. So it clearly is global.
In your example, the view would just tell the application for example: "Add a todo". The reducer will intercept this message and return an array with the added todo. This returned array is what will be saved in the store.
If you want a seperate "todo", this will probably refer to the currently "active" to do. Marking it as such will make the purpose more expressive.
This is single state because your root reducer will end up with something like:
{
"activeTodo": activeTodoReducer
"todos": todosReducer
}
And you can access these key / values in your components throughout the entire application.
How can redux be called single state if each reducer have its own individual state.
Because the state is not saved in the reducers. The state is only saved in the store and there is only one store. That is why is called single state.
To create the store:
const store = createStore(myBeautifulReducers)
In your case, myBeautifulReducers would be:
const myBeautifulReducers = combineReducers({reducer, reducer2});
myBeautifulReducers will be an object that contains both reducers (reducer and reducer2) and the logic you wrote in each of them (switch statements and so on).
what i want to do is dispatch an action in my set interval function and not in get initial props and save my data in store and how to get that data back from store in react app it was simple just import action form action file and call like this this.props.actionName() but how do i do this in next and to get data from store we map state to props how can it be done in next thanks here my function which i want to implement in
this.fetchCryptoData().then(data => {
var Keys = Object.keys(data.DISPLAY);
this.setState(
{
crypto_head_coins: Keys
},
() => {
// // this.props.update_array([]); // update_array() is my action i haven't imported it
let rate_updated = [true, true, true, true]; // i want my store updated_array data here
for (let i = 0; i < this.state.crypto_head_coins.length; i++) {
//my code here
// this.props.store.dispatch(update_rate_array(rate_updated)) //it says cant read property
// of dispatch of undefined
// i want to dispatch my action here not in getinitialprops
this.setState({ rate_updated });
}
);
});
I use NextJS sometimes, It is the same as a Create-React-App essentially.
I just noticed your question does not include 'React-Redux', You will need to install/save 'React-Redux' and 'Redux' to use connect/dispatch, etc. I have a sample boilerplate on Github.
Another missing piece for converting this into an action.. is perhaps redux-thunk, to handle promises.(Try without it first.)
More information on redux-thunk here.
https://github.com/reduxjs/redux-thunk
You are setting state twice(once in the callback of another), which is going to cause multiple re-renders. (Unless ShouldComponentUpdate is implemented) Might want to re-consider this design.
Implement your MapDispatch to Props
After doing so you can simplify the line calling it, like the below using destructing.
// this.props.store.dispatch(update_rate_array(rate_updated)) //it says cant read property
let update_rate_array = {this.props}
update_rate_array(rate_updated)
You should implement your MapDispatchToProps removing some complexity in the naming and calling.
I have uploaded some simple examples to Github, and there is also an identical related CodeSandbox.
To receive your updated information from State, use MapStateToProps.
Example here.
If different reducers are associated to the same action and are performing changes to the store, are they receiving the same version of it before any edit happens? Is there a priority to consider?
Example:
Lets assume you dispatch an action Update to edit an entity on server side, once data is successfully updated, an effect will dispatch an Update_Success action with an instance of the newly received version of entity as payload. A first classic and logical reducer will use it to update the store:
case ItemActionTypes.UPDATE_ITEM_SUCCESS: {
const item = action.payload;
return {
...adapter.upsertOne(item, state),
loaded: true,
loading: false
};
}
Now lets assume that in a different file, you have a different reducer associated to the same action and needs to compare the newly received entity against the old one in store:
case ItemActionTypes.UPDATE_ITEM_SUCCESS: {
const item = action.payload;
const oldItem = state.entities[item.id];
const changes = getAffectedProperties(item, oldItem);
// ...
}
The question is: Is there any chance oldItem is actually holding the newly received item instead of the old one as the first reducer may have already updated it? Is it a first come first serve case? or is there anything on its implementation that guarantees all reducers are performing changes to the the same version of the store and maybe merging those changes at a further step?
If different reducers are associated to the same action and are
performing changes to the store, are they receiving the same version
of it before any edit happens? Is there a priority to consider?
Reducers only have access to their piece of state. So you're not supposed to really care about that.
Also, when one action is dispatched, all the reducers are called in a synchronous way. Which means that if for the ACTION_A you're modifying your store from different places (/ different reducers), your selectors (or even the view), will only be refreshed once. When all the updates for that action are applied on the store.
I am new to react js.
I have two routes like A & B. Now i am passing some values from A to B as props. If B page is refreshed, then all props values from A is gone and B page is not rendering. I am using react with redux.
mapDispatchToProps & mapStateToProps functions are used to pass values between A & B routes as props.
For example: Route A has done some calculations and store the values in redux state and Route B is exported as connect(mapStateToProps, mapDispatchToProps)(B), by using mapStateToProps in which A's state values are passed to B as props.
Please suggest me the best way to handle browser refresh on above mentioned use case and also if any other best way to pass the values between routes. Thanks in advance.
Your question talks about two different concerns. First is passing props from one page to another in a React/Redux application, and second is maintaining the application state when the page is refreshed.
You've described the correct method of passing data between two routes in a redux based application.
Which brings us to the second concern.
How to maintain the state of a React/Redux application when the page is refreshed?
When a React/Redux application is refreshed, it gets initialised again and the redux store gets it's default values.
If you wish to maintain the app state across page refreshes or across different sessions, you need to store the state somewhere, and load it when the app initialises.
We can divide this problem into three parts:
Where to store the data
How to store redux state
How to reload the data when the application is initialised
Let's look at each sub-problem individually.
Where to store the data?
You can use the Web Storage API to store data within the user's browser. This API provides 2 mechanisms to store data:
sessionStorage: Stored data is preserved as long as the browser is open, including page reloads and restores.
localStorage: Data is preserved until it is cleared by the user or the application. It persists even if the browser is closed and reopened.
Both sessionStorage and localStorage allow you to store key-value pairs in the browser, and both provide the same set of functions to manage data.
For sessionStorage (example taken from MDN):
// Save data to sessionStorage
window.sessionStorage.setItem('key', 'value');
// Get saved data from sessionStorage
var data = window.sessionStorage.getItem('key');
// Remove saved data from sessionStorage
window.sessionStorage.removeItem('key');
// Remove all saved data from sessionStorage
window.sessionStorage.clear();
For localStorage:
// Save data to localStorage
window.localStorage.setItem('key', 'value');
// Get saved data from localStorage
var data = window.localStorage.getItem('key');
// Remove saved data from localStorage
window.localStorage.removeItem('key');
How to store redux state?
As you are already aware, Redux provides a createStore function which takes our root reducer and returns the application store.
The store object holds the entire application store, and provides a few methods including one to register a listener.
store.subscribe(listener) can be used to add a change listener to the store, which will get called every time the store gets updated.
We will add a listener to the store, which will save the application state to localStorage.
Try adding this in the file where you create your store using createStore:
/**
* This function accepts the app state, and saves it to localStorage
* #param state
*/
const saveState = (state) => {
try {
// Convert the state to a JSON string
const serialisedState = JSON.stringify(state);
// Save the serialised state to localStorage against the key 'app_state'
window.localStorage.setItem('app_state', serialisedState);
} catch (err) {
// Log errors here, or ignore
}
};
/**
* This is where you create the app store
*/
const store = createStore(rootReducer);
/**
* Add a change listener to the store, and invoke our saveState function defined above.
*/
store.subscribe(() => {
saveState(store.getState());
});
How to reload the stored data, and restore the application state when the app is initialised again?
When we create our app store using createStore, we have the option to pass an initial state to the store using the second parameter to the function.
When the application starts up, we will check the localStorage for any saved data. If we find it, we will send it as the second parameter to createStore.
This way, when the app finishes initialising, it will have the same state as it did before the page was refreshed or the browser was closed.
Try adding this in the file where you create your store using createStore:
/**
* This function checks if the app state is saved in localStorage
*/
const loadState = () => {
try {
// Load the data saved in localStorage, against the key 'app_state'
const serialisedState = window.localStorage.getItem('app_state');
// Passing undefined to createStore will result in our app getting the default state
// If no data is saved, return undefined
if (!serialisedState) return undefined;
// De-serialise the saved state, and return it.
return JSON.parse(serialisedState);
} catch (err) {
// Return undefined if localStorage is not available,
// or data could not be de-serialised,
// or there was some other error
return undefined;
}
};
/**
* This is where you create the app store
*/
const oldState = loadState();
const store = createStore(rootReducer, oldState);
That's it! Now, combine the last two blocks of code, and your application has the ability to maintain state across page refreshes, or even across browser restarts.
Hope this helps.
Cheers!
:)
you can try redux-persist or redux-storage ,
when you initialize the store
createStore(reducer, [preloadedState], [enhancer]),
you can get the data and assign it to preloadedState
Try using react-router and this will render components based on route.
When the app is initialized, app should fetch data and update the store with the required information.
Eg: In the below example, When IntilizeApp component is mounting compute the information required and update the store by dispatching the actions. Use react's life cycle method like componentWillMount to compute.
import {Router, Route, hashHistory} from 'react-router'
// import initializeApp
// import ComponentA
// import ComponentB
<Router history={hashHistory}>
<Route path="/" component={InitializeApp}>
<Route name="A" path="A" component={ComponentA} />
<Route name="B" path="B" component={ComponentB} />
</Route>
</Router>