Update array on state after any action of my react-redux reducer - arrays

I am learning react-redux at the moment, already grasped the basics (I believe), I am now trying to create a notification array on my state that can be populated by several actions.
But since is a notification, I only want to add a notification after each of those actions is completed (the state is changed).
I thought about using a middleware class but that would add the notification into array before the action is computed on the reduce.
Quick example:
User logins -> dispatch action -> affects state property related to login -> adds a notification on the notification array
User adds item -> dispatch action -> affects state property related to items -> adds a notification on the notification array
These are 2 different actions, I could in the if logic of each action (on reducer) update the notification array, but seems like repeated code and I would be creating notification object on a reducer, is that okay?
So in this case where should I dispatch the action of adding a notification to the notification array?
I am looking for best practices

The best practice is to handle such scenario's in reducer. Process the data one-time and update state accordingly. In your scenario, you want to update the notifications array once the action is completed. You have to do that repeatedly because it is to be done. Here is what you can do:
case LOGIN:
//checks if user is valid
return {
...state
loggedIn: true,
user,
notifications: state.notification.push({ id: 1, message: "Logged In!" })
}
And if you want to remove a notification you can do that:
case REMOVE_NOTIFICATION:
// You can get the notification ID you want to remove from action
let notifications = state.notification.filter(notify => notify.id !== action.id);
return {
...state
notifications
}
Hope this will help you.

Related

any way to know the latest action that was dispatched in redux

I am working on a react-with-redux application I am working with the library redux-undo and as the capability of the library goes, it listens to a list of actions and reverts to the previous state when the undo happens.
Scenario: I have a page where a list item will be created/deleted and a API call is made whenever these actions happen. The user can undo both the create and delete operations.
I would like to know if there is any way to know the latest action that has been dispatched.
For example: If the user creates a list item and clicks undo, I would like to know that the latest action that was dispatched was create, so that I can revert the create(delete the list item by making an API call).
Like wise, If the user deleted an list item I would like to know that the latest action that was dispatched was delete, so that I can revert the delete(create the list item again by making an API call, fetching the details from past shape of state and sending the details of the deleted list item)
Please let m know if there is any way to achieve this?
You can use store.subscribe, as stated here:
The easiest way is to have a reducer that remembers just the last action:
function lastAction(state = null, action) {
return action;
}
Then you can use store.getState().lastAction, assuming you did
something like
import { combineReducers, createStore } from 'redux';
const rootReducer = combineReducers({
someReducer,
someOtherReducer,
lastAction // <-- use it!
});
const store = createStore(rootReducer);
store.subscribe(() => {
console.log(store.getState().lastAction);
});
You would need to find an alternative storage space to store your latest action that is unaffected by redux-undo but also global so you can access it anywhere you need.
I recommend a local storage solution.
In your reducer, you can add a statement to set which was the latest dispatched action into browser storage:
...
case CREATE: {
localStorage.setItem("latestAction", "CREATE");
return someNewState;
}
case DELETE: {
localStorage.setItem("latestAction", "DELETE");
return someNewState;
}
...
Then when you want to get that value from anywhere in code:
localStorage.getItem("latestAction");

Storing state (loading, error ...) about a specific entity in a Redux store

TLDR; Individual entities in a store can be in different states, including loading and error states. How can we correctly reflect this in our UI with a "fire and forget" approach to Redux actions, while preferably keeping the behavior of our action creators consistent?
The convention is to "fire and forget" an action and subscribe to store updates, as opposed to dispatching an action and subsequently dealing with its return value or promise.
Several examples illustrate this:
// This is a web app that lets users create and book events
const loadEvents = () => dispatch => {
dispatch(loadEventsRequest());
return fetchFromApi('https://...')
.then(
json => dispatch(loadEventsSuccess(json)),
error => dispatch(loadEventsFailure(error))
)
}
componentWillMount() {
this.props.loadEvents(); // Fire it; the state will be updated eventually
}
render() {
return this.props.events.map((event) => ( <Event event={event} /> ));
}
The list of events can be in several states, e.g. a loading state. We can accommodate this by designing our state like this:
entities: {
events: {
1: {
title: "Movie night"
},
...
}
},
visibleEvents: [
isFetching: false,
ids: [1, 2, 3]
]
It's easy to show a loading indicator based on the value of visibleEvents.isFetching.
State for specific entities
Let's imagine that events can be booked, canceled and deleted. All of these actions may result in errors from the backend ("The event is fully booked") or success scenarios ("The event was booked"). We can notify the user in two ways:
Alternative 1)
Dispatch the action from the component and respond to it using then/catch. Caught an error? Display it. The state stays local.
For this to work our loadEvents() action creator would need to be changed. Currently it catches errors and fires loadEventsFailure(), so our component doesn't know whether the action failed or succeeded (can't be caught).
We could re-throw the error from the action creator or reject the promise, (or don't catch it at all), so that our component gets a chance to catch and respond to it. My biggest concern is that we end up with inconsistent behavior across our action creators -- some throw errors, some don't. We could make it a convention to always throw errors, but unless the component catches the them we end up with "Uncaught" errors all over the place.
It doesn't feel right to let the component decide the behavior of the action creator (whether to throw/reject), especially if other components want to use it as well. Also, there would be no use for a booking reducer in this case because our state never needs to be updated during the booking process.
Alternative 2)
Store every type of result (error or success state) in the Redux state together with their specific entities (i.e. each event can have multiple states related to booking, deletion and cancellation).
Our component wouldn't need to "respond" to the action creator; it could simply fire an action and read the result off the Redux state (pretty idomatic). Conceptually:
handleBookingButtonClicked() { this.props.bookEvent(id); }
// ...
render() { if (store.entities.events[id].bookingError) return (<div>Booking failed</div>); }
All components that relate to booking of an event can read bookingError. Components related to cancellations can read cancellationError, etc.
(Note: If it's tempting to only have one "global" error object in the store, or one error object per entity which contains any type of error related to it, this would quickly fail if you want to display several components simultaneously.)
A couple of issues with this approach:
Eventually the errors would need to be cleared; we don't want to display an error indefinitely.
There could potentially be a lot of different state indicators for each event: loading, booking, deletion, cancellation, updating etc. In addition, each of these indicators can be in different states: Booking an event? It could succeed, fail or be in progress. And if it fails, we want to know why.
Two different components that alllows booking would display the same error.
Which approach would you recommend, and how would you address the issues described (and the proposal to have consistent action creators)?

State changes in Redux-Saga

I have a simple React App. It allows a user to edit a form to update data in a database. On Submit the page generates an action with the form data. A Redux Saga yields to the action and asynchronously updates the database. This all works.
However in one case the update is slightly more involved. Not only must new data be added but any data deleted on the form must be deleted from the database in a series of API calls. To do this I need to know what has changed (e.g. what has been deleted) rather than just the new state.
How can my saga have access to this information? I could calculate it in the reducer because that has access to the previous state but it is commonly held to be an anti-pattern for the reducer to then dispatch a new action.
Sagas have a select effect available, which just runs a selector function and returns the extracted state. You can use this to retrieve the old and new items from the Redux store, and deal with the changes from there:
function* handleFormUpdates() {
const oldItem = yield select(selectOldItem);
const newItem = yield select(selectNewItem);
const changes = diffTheItems(oldItem, newItem);
// make API calls to deal with changes appropriately
}
Overall, this is a good reason to keep the "temporary" or "draft" state in Redux, so that you can make use of both the "current" and "draft" values in your logic.
I discussed some related concepts in my blog posts Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers and Practical Redux, Part 8: Form Draft Data Management
...any data deleted on the form must
be deleted from the database in a series of API calls. To do this I
need to know what has changed (e.g. what has been deleted) rather than
just the new state.
If I understand correctly you have form state saved in a redux store and you need to know when and what has changed. You could create your own watcher saga:
function* watchFormUpdates(){
while (true) {
const oldFormState = yield select(selectors.selectFormState);
const action = yield take (actionTypes.FORM_UPDATE); // wait until action is dispatched
const newFormState = yield select(selectors.selectFormState); // at this point store has been updated
// compare oldFormState with newFormState...
if(oldFormState.hasSubscription && !newFormState.hasSubscription) {
yield fork(deleteSubscriptionSaga); // start non-blocking worker task
// or just dispatch action - yield put(actionCreators.deleteSubscription());
}
}
}

using multiple redux stores one for each app user

in a react native app, i'm using redux. currently the whole app have single store and i use redux-persist to cache store to localstorage.
my app is username and password protected, you must create account to use it.
now i want to provide ability so that my user can switch between his accounts -if he have more than one account- . this is causing lots of trouble because now i have to clear storage and reset state everytime user switch between accounts.
so i was considering may be i can use multiple stores, one for every users ?
for example my app state looks like
{
chat:{},
highscores:{},
gameHistory:{},
}
now if a user have account lets say User1#gmail.com the state will be populated with his data. and his state will be saved to LocalStorage,
once he switch account to User2#gmail.com now i have to reset the app to its initialState, then somehow load the User2 state from localStorage
i dont want the state of the app to be lost everytime user switch between accounts.
so i was considering may be in this case it would be a good option to use a multiple Redux Stores, one for every user.
did anyone had an app that is designed to be used by multiple users before ?
how can we do this in redux ?
Well Answer above work fine, but since i'm using ImmutableJs, having a deeply nested objects can really be hard to handle.
so i ended up namespacing the Storage Key with user_id.
so now when ever i switch user, i just flush the whole store with this specefic user data from localStorage, or AsyncStorage.
i wrapped rootReducer in a simple reducer to handle this.
function makeRootReducer(rootReducer){
return function reducer(state, action){
if(action.type==='SWITCH_USER'){
//LOAD USER DATA..
const data = JSON.parse(localStorage.getItem("store.user."+action.id)||"{}");
return makeInitialData(data); //this just return initialData.
}
let newState = rootReducer(state, action);
//simple save state to localStorage if state changed
if(state !== newState)localStorage.setItem('store.user.'+state.user_id',JSON.stringify(newState);
return newState;
}
}
I don't think having a store for each user is a good idea. See this SO answer: https://stackoverflow.com/a/33633850/3794660
Why don't you namespace the data you have in your reducer by user id? Something like this:
{
currentUserId: "1",
chat:{ "1": { // Chats for user id 1 }, "2": { // Chats for user id 2 }},
highscores:{ // Same structure as above },
gameHistory:{ // Same structure as above },
}
When you switch user account, you simply update the currentUserId in the state.
I'd recommend using selectors to encapsulate the logic to read the data from the store.
A simple selector to get all the chats for the current account could look like this:
const getCurrUserId = state => state.currentUserId
const getChats = state => {
const userId = getCurrUserId(state);
return state.chat[userId];
}
You then use your simple getChats selector in your mapStateToProps to pass the data to your components. In this way you encapsulate the logic to retrieve the data from the state and your components don't need to know these details, so you're free to change your strategy if you need to.

Dispatching an action after Store state change in ReactJS (with Alt)

My application is a simple file explorer, and has three components:
A Tree: representing a directory tree created by user;
A Simple page: with the selected directory content;
A Notification Component: to handle messages triggered by user actions.
When the user creates a new folder, the file store (containing all data related to both components) is updated with the server response and notifies the components, with is working fine.
But I need to trigger an Action that will add a message with the result to NotificationStore. This should be an easy task, but I'm trying to do this for a long time, with no success.
The closer I got to solve was creating a "store.listen()" inside the componentDidMount() and then calling my Action (adding the notification message), which was successfully triggered. But it result in an error: "Cannot dispatch in the middle of a dispatch".
Is there any way to call an action after store state change?
Update:
An alternative could be binding the NotificationStore with FileActions and then use the "waitFor(FileStore.dispatchToken)" and THEN set the message in the Store. It could work in this case, but maybe not in others.
In your store that need to listen to NotificationStore action, add an listener:
this.bindListeners({
onActionSomething: NotificationAction.sendNotifcation
});
this will take trigger function in this store called onActionSomething after action sendNotifcation in NotificationActions.
Do not forget to waitFor the dispatcher from other Notification:
onActionSomething(data) {
this.waitFor(NotificationStore);
this.pages = data;
}
Data parameter will be the same as in Notification one

Resources