Creating generic reducers in redux toolkit - redux

Our app has the same data structure for network requests in multiple slices across the codebase. So they have data, error, meta fields, and they are all updated in the same way in response to thunk actions.
At the moment I can't find a way to do this in with redux toolkit without duplicating the reducer logic. Is there a way to create generic reducers that I can just spread into the reducers property of createSlice?
initialState,
name: 'sliceName',
reducers: {
...createFetchReducers(someAsyncThunk),
},

Related

How to properly convert to redux toolkit a pattern where the same action is dispatched/checked on several places

I am slowly migrating my app from plain redux to redux-toolkit.
One patter I noticed I have is as follows:
An async thunk that fetches stuff and dispatches start/complete/error actions
A middleware that reacts to the complete action
Other functions that also dispatch the complete action
Previously I converted other thunks to be a simple createAsyncThunk action thunk and react to the generated thunk states fulfilled|pending|rejected, however on this situation I need to be able to dispatch the fulfilled one from several places.
I don't think is idiomatic (nor a good idea) if I dispatch the fulfilled type manually by doing dispatch({ type: myThunk.fulfilled, payload}).
My guess is that I should create another normal sync action creator and dispatch that from the thunk, from the other several places and use that one to react to it on the store and the middleware and totally ignore the thunk.fulfilled state.
Is my assumption correct?

Can a redux-toolkit createSlice use a js Map as state?

In general, using a mutable object such as Map is strongly discouraged.
However, the magic of immer allows immutable objects to be manipulated as though they are mutable.
Specifically, immer supports immutable versions of Map using enableMapSet
In redux-toolkit createReducer and createSlice wrap state manipulation with immer's produce.
Collectively, I think these facts mean code like this should be safe:
import { createSlice } from '#reduxjs/toolkit'
export const testmapSlice = createSlice({
name: 'testMap',
// Using a Map() as redux state
initialState: new Map(),
reducers: {
add: (state, action) => {
state.set(action.payload.identity, action.payload)
},
},
})
However, when I use this in a React component, I get the polite error message A non-serializable value was detected in the state, in the path: `testMap`. Value: Map(1) {"A" => {…}} Take a look at the reducer(s) handling this action type: testMap/add..
Is there a way to safely use Map without getting this error message?
Define "safely" :)
In theory, you could put anything you want into the Redux store.
In practice, per that FAQ, non-serializable values are likely to cause things like the DevTools to break (which defeats much of the purpose of using Redux in the first place). Use of Maps and other mutable instances are also likely to cause portions of your UI to not re-render correctly, because React-Redux relies on reference checks to determine if data has changed. So, we specifically tell users that you should never put non-serializable values in the Redux state.
In this particular case, you should be able to use a plain JS object as a lookup table rather than a Map, and accomplish the same behavior.
As an absolute last resort, you can turn off the serialization checking for certain parts of the state, but we strongly discourage folks from doing that.
I think it is not safe to use Map() on the state because Redux is already designed to avoid the mutation at the level of Reducers, and when you use createSlice() it even takes care of it in the background. Your idea of a double security at the State level may appear to be another issue that you provoke. It might either provoke the UI not to update. or throw an error
(Ps: This is purely analogic. I have not tried it)

Exclude redux-form reducer from main store

import { combineReducers } from 'redux'
import { reducer as form } from 'redux-form'
combineReducers({
router: connectRouter(history),
form,
....
// huge amount of other reducers
})
Above you can see combined reducers in one store. As bigger it becomes as slower it becomes because on each action it should make many more checks as it was at the beginning. As you also can see I use redux-form for handling state in my form. And it starts to be slower and slower when I type in redux-form fields. I want to find out is there any way to create some individual store for redux-form, and prevent this from slowing down. Or is there exist some other solutions to this problem?
Using redux you can have multiple stores.
redux-form is using connect API from react-redux to get info from store.
With React Redux, the wrapper classes generated by the connect() function do actually look for props.store if it exists, but it's best if you wrap your root component in and let React Redux worry about passing the store down. This way components don't need to worry about importing a store module, and isolating a Redux app or enabling server rendering is much easier to do later.
https://redux.js.org/faq/store-setup#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself
I'm a redux-form collaborator.

In Redux: how to store data from initial html to store? initialState or dispatch?

We have a dillema.
Certain data arrives through a template to our initial index.html page.
We put it on the window and the want to place it in the store.
Right now, we have something like this (psuedoish):
class App ... {
componentDidMount() {
this.props.setLoggedInUser(this.props.user);
// setLoggedInUser is a dispatch prop
}
}
<App user={window.user} />
option two is to just use initial state:
createStore(reducer, { user: window.user }, ...)
We had a lengthy discussion and can't agree if initialState is an anti-pattern or not.
Which is the correct way to implement this type of data loading?
I think initialState is not intended for such use. I would probably initialize it in reducer:
userReducer(state=window.user, action) ...
But that is under assumption that window.user is constant. If it is floating kind of thing, than I would probably go dispatch way, but not in componentDidMount, but in piece of code immediately following createStore
Using initialState when creating the store from bootstrapped data is the preferred way according to the documentation (see #2).
When bootstrapping data this way, you never get in a state where you're waiting for the store to apply the dispatched action. Another bonus is that you don't dispatch an action that isn't in relation to something happening in the UI.

Composing higher order reducers in Redux

I've created some factory functions that give me simple (or more advanced) reducers. For example (simple one - base on action type set RequestState constant as a value):
export const reduceRequestState = (requestTypes: RequestActionTypes) =>
(state: RequestState = RequestState.None, action: Action): RequestState => {
switch (action.type) {
case requestTypes.start:
return RequestState.Waiting;
case requestTypes.success:
return RequestState.Success;
case requestTypes.error:
return RequestState.Error;
case requestTypes.reset:
return RequestState.None;
default:
return state;
}
};
Using those factory functions and combineReducers from redux I can compose them into fully functional reducer that handles most of my casual actions. That gives me readable code and prevents me from making silly mistakes.
Factories are good for common actions but when I need to add some custom behavior (for action type) which should modify some part of the store significantly I would like to write a custom part of the reducer that will handle that action for me.
The idea is to compose reducers in an iterate manner, so combineReducers but for an array. This way I could use my factories creating reducer and then combine it with my custom reducer that handles some specific actions. The combineReducers for an array would then call the first one, recognize that nothing has changed and call the second (custom) one to handle the action.
I was looking for some solution and found redux-actions but do not quite like the way it links actions and reducers making the semantics little different from what I'm used to. Maybe I do not get it, but eventually I like to see that my reducer is written as pure function.
I am looking for some hint that will show me the way.
Is there any library or project that uses any kind of higher order reducers and combines them in some way?
Are there any downsides regarding composing reducers like described above?
Yep, since reducers are just functions, there's an infinite number of ways you can organize the logic, and composing multiple functions together is very encouraged.
The "reducers in an array" idea you're looking for is https://github.com/acdlite/reduce-reducers. I use it frequently in my own app for exactly that kind of behavior - running a combineReducers-generated reducer first, then running reducers for more specific behavior in turn.
I've written a section for the Redux docs called Structuring Reducers, which covers a number of topics related to reducer logic. That includes useful patterns beyond the usual combineReducers approach.
I also have a list of many other reducer-related utilities as part of my Redux addons catalog.

Resources