Redux will be dispatching actions for state change. What are naming conventions of action types in redux?
There are a few conventions around the community, I'll list the ones I know of and think are useful here:
The most common convention is to keep the action types ("event types") in CONSTANT_CASE.
This avoids spelling errors, where the action has a type of my_type, but the reducer expects a type of my-type or My_Type.
Another very common convention is to save the action types in a separate file as constants, e.g. var MY_ACTION_TYPE = 'MY_ACTION_TYPE';, and to use them from there.
This also avoids spelling errors, so you don't expect an action to have a type of MY_ACTION_TYP. If the variable doesn't exist, you'll get an error immediately, especially if you're linting.
A not quite as common, but imho very useful, convention is to scope the actions to a project and a domain. This approach was popularized by Erik Rasmussen in his "Ducks" proposal, which specifies that action types have to be in this shape: var MY_ACTION_TYPE = 'appname/domain/MY_ACTIONTYPE'.
This avoids the case of two action constants having the same value. E.g. imagine you have a admin area and user facing area, and both have forms dispatching a 'CHANGE_USERNAME' action type. This will make two reducers pick up the same action, where one shouldn't pick the other one up. This can happen on accident, and is very annoying to track down. By prefixing them with the app- and domain name, one avoids this issue: 'appname/admin/CHANGE_USERNAME' is different from 'appname/user/CHANGE_USERNAME'!
That's all the conventions I know of and use, but I'm sure somebody else has more – what have you used and found useful in your projects?
There are also some conventions around naming asynchronous action types. If you have a set of actions to represent an api call to get a user, you can split them up into something like:
FETCH_USER_REQUEST - for when you first send the api call
FETCH_USER_SUCCESS - for when the api call is done and successfully returned data
FETCH_USER_FAIL - for when the api call failed and responded with an error,
FETCH_USER_COMPLETE - sometimes used at the end of the call regardless of status
There is a new pattern that addresses this, redux-auto.
It takes the ideas of reducer composition one step further. Where instead of having a file that represents your reducer and creating individual actions functions.
redux-auto's approaches to have folders with individual JS files representing each action/transformation on the state and dynamically exposing this as functions
example
└── store/
├──user/
│ └── index.js
│ └── changeName.js
└──posts/
└── index.js
└── delete.js
Now from anyway in your app you can write
import actions from 'redux-auto'
...
actions.user.changeName({name:"bob"})
store/user/changeName.js
export default function (user, payload) {
return Object.assign({},user,{ name : payload.name });
}
Thats is!
If you want to listen for redux actions in third-party reducers. You can use as loose quality check against the function.
action.type == actions.user.changeName // "USER/CHANGENAME"
For something more advanced you can even see if an action is owned by a specific reducer
// Returns true if it's an action specifically for user
if(action.type in actions.user)
You can read more on the project page
See Redux official styleguide: https://redux.js.org/style-guide/style-guide/#model-actions-as-events-not-setters
Model Actions as Events, Not Setters
Write Meaningful Action Names
Related
Hello this is my first question. I am trying to set up a project where modules along with the redux and sagas will be injected into the main app, using redux-injectors. In my sagas I want to use yield select, to check if an action has updated the state and then carry on. For example, when I post an image, I want to make sure there were no errors in posting the file and then move on. I use the following function:
export const imageErrors = (state: RootState): IImagesErrorState => state.image.errors
and then in the saga.ts file I use it as such:
if (imagesErrors?.postImageError !== null) {
throw imagesErrors.postImageError
}
this works fine as long as the state.image exists in the root state from the beginning. However, how do I do that when I want to inject this state later on using useInjectReducer and useInjectSaga? I obviously get an error
Property 'image' does not exist on type 'Reducer<CombinedState<{ user: CombinedState<{ auth: IAuthState; errors: IErrorState; }>; }>, AnyAction>'.ts(2339)
So how do we handle selectors of specific pieces of state, since state does not yet include them?
Thank you so much.
Can't talk about the Typescript part of things, but in terms of architecture you've got two options.
One is the obvious - that is to add conditions or ? everywhere to avoid errors from accessing missing properties, but that can get tedious quickly.
The other probably better option is to rethink your state & application chunks. What is this saga that is accessing state that isn't existing yet? Does it need to run before you have such state? If not, let's move the saga to the same chunk as the reducer. In the opposite case, where you need the saga to be running e.g. as part of the runtime chunk, then perhaps the image state should be in the runtime chunk as well.
TL;DR: In case of a reusable component which has some complicated logic for managing its own state (think: a facebook comment textarea with autocompleter, emoji etc) how does one use store, actions and reducers to manage the state of multiple instances of this component spread across whole website?
Consider the real-world example from the official redux repo.
In it we have:
a RepoPage, which displays list of users who have starred a particular repo,
a UserPage, which displays a list of repos which are starred by particular user
a List, which is generic enough that it can display list of users or repos, provided the items and way to renderItem. In particular RepoPage uses User component to display each of users who starred the repo, and UserPage uses a Repo component to display each of starred repos.
Assume that I really want all of the state to be in Redux.
In particular, I want the state of every List on every RepoPage and UserPage to be managed by Redux. This is already taken care of in the example, by a clever three-level deep tree:
at the top level the key says what kind of component data is it (in the example it is called store.pagination)
then there is a branch for each particular type of context in which the component can be (store.pagination.starredByUser, store.pagination. stargazersByRepo)
then there are as many keys as there are unique contexts (store.pagination.starredByUser[login], store.pagination. stargazersByRepo[repo])
I feel that these three levels correspond also to: component type, parent type, parent id.
But, I don't know how to extend this idea, to handle the case in which the List component itself had many children, with a state worth tracking in Redux.
In particular, I want to know how to implement a solution in which:
User component remains intact
Repo component has a button which toggles its background color
the state of each Repo component is managed by Redux
(I'm happy to use some extensions to Redux, which still use reducers, but don't want to go with "just keep it in React local state", for the purpose of this question)
My research so far:
it looks like in Elm the Actions (messages) are algebraic data types which can be nested in such a way, that a parent component can unpack an "outer envelope" of the message and deliver a inner action intended for child to the child reducer (updater).
since it is a convention in Redux to use a string as the type of action, a natural translation of the above idea is to use prefixing, and this seems to be what prism (foremly known as redux-elm) does: the action.type is comprised of substrings which tell the path through components' tree. OTOH in this comment the prism author tomkis explains that the most important part of Elm Architecture that Redux is missing is composition of actions
the two above approaches seem to be expanded versions of approaches described in Reusing Reducer Logic
I haven't fully grasped how redux-fly works internally, but it seems to use the payload, not the action.type to identify a component instance by its mounting path in the store which also corresponds to a path in the components tree because of the way it is constructed manually by components
WinAPI, which to me seems quite similar to Redux if you squint, uses unique hWnd identifier for each control, which makes it super easy to check if action was intended for you, and decide where should be your state in the store.
The above idea could probably lead to something described in Documentation suggestion/discussion: Reusing Reducer Logic where each type of component has its own flat subtree indexed by unique id.
Another idea descibed in the linked thread linked above is to write a reducer for a particular type of component once, and then let the reducer for the parent component call it (which also means, that the parent is reponsible to decide where in the store the state of the child is located - again, that seems similar to Elm Architecture to me)
A very interesting discussion More on reusability for custom components in which details of a proposal vary similar to the one above is presented
in particular above discussion contains a proposition by user nav, to organize the store tree recursively in such a way, that a state of a component is subtree in two kinds of branches: one for private stuff, and the other for "tables" of child components, where each class of child component has its own "table", and each instance of child has a unique key in that table, where its state is recursively stored. The unique keys which give access to these children are stored in the "private" section. This is really similar to how I imagine WinAPI :)
another elm-inspired proposition by user sompylasar from the same thread is to use actions which contain actions for children as a payload in a "matrioshka" style, which in my opinion mimick how algebraic types constructors are nested in Elm
redux-subspace was recommended in discussion about Global Actions for prism, as a library which is both Elm-inspired and lets you have global actions.
I will try to explain one of idea which is inspired by Elm lang and has been ported to Typescript:
Let's say we have very simple component with the following state
interface ComponentState {
text: string
}
Component can be reduced with the following 2 actions.
interface SetAction {
type: 'SET_VALUE', payload: string
}
interface ResetAction {
type: 'RESET_VALUE'
}
Type union for those 2 actions (Please look at Discriminated Unions of Typescript):
type ComponentAction = SetAction | ResetAction;
Reducer for this should have thw following signature:
function componentReducer(state: ComponentState, action: ComponentAction): ComponentState {
// code
}
Now to "embed" this simple component in a larger component we need to encapsulate data model in parent component:
interface ParentComponentState {
instance1: ComponentState,
instance2: ComponentState,
}
Because action types in redux need to be globally unique we cannot dispatch single actions for Component instances, because it will be handled by both instances. One of the ideas is to wrap actions of single components into parent action with the following technique:
interface Instance1ParentAction {
type: 'INSTNACE_1_PARENT',
payload: ComponentAction,
}
interface Instance2ParentAction {
type: 'INSTNACE_2_PARENT',
payload: ComponentAction,
}
Parent action union will have the following signature:
type ParentComponentAction = Instance1ParentAction | Instance2ParentAction;
And the most important thing of this technique - parent reducer:
function parentComponentReducer(state: ParentComponentState, action: ParentComponentAction): ParentComponentState {
switch (action.type) {
case 'INSTNACE_1_PARENT':
return {
...state,
// using component reducer
instance1: componentReducer(state.instance1, action.payload),
};
//
}
}
Using Discriminated Unions additionally gives type safety for parent and child reducers.
My team and I are busy designing a redux store, including all the possible actions that can be fired. Due to the nature of our application, we have some inherent coupling/dependencies between different nodes (branches?) of our redux store. Our idea of tackling these dependencies in the most scalable manner was to extract it to its own separate node in the state tree. Let me give a simplified example to illustrate the structure we have in mind, and get to the problem that we're facing:
Say our state tree has the following:
SectionA: with data A as a bool
SectionB: with data B as an int
DependencySection: with a dependency that if A is true then B has to be 10 or larger
You're probably thinking, why do it this way? Why not just integrate the dependency into the reducer for SectionB? The reason is that these dependencies vary per client, and we're reading them from a database. It can link any part of the state tree to any other part and have its own actions that need to happen.
Now, my question is, how do we reduce actions while taking into account these dependencies? If our application flow is:
User causes TOGGLE_DATA_A action
SectionA reducer updates data A
DependencySection updates data B based on the dependency
What happens if we introduce a SectionC, with data C that is dependent on data B? It seems like a new action needs to be fired when the DependencySection updates data B, with the action being that data B has been updated. This would mean firing an action while another action is being reduced, which is obviously not allowed. Alternatively, it seems like making the different reducer sections execute in a VERY specific order would also solve the issue, but this is surely an anti-pattern.
The only solution we can come up with is to have middleware that repeatedly fires UPDATE actions after every action until the state no longer changes. For example, after the TOGGLE_DATA_A action updates data B (via the dependency on data A), the next UPDATE action would update data C (via the dependency on data B), and the next UPDATE would update nothing, stopping the loop. This is quite hacky.
So, is there a better way to deal with reducing such a dependent state? Or should we be structuring our state tree differently?
One of the key concepts behind Redux is that reducer logic is just functions, and if you need to order your state update handling in a specific order, you can do that yourself by writing explicit code for that. So no, "making different reducer sections execute in a specific order" is not an "anti-pattern", it's absolutely a valid and encouraged approach with Redux.
There's examples and discussions of this approach in the Structuring Reducers - Beyond combineReducers section of the Redux docs, and in my blog posts Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent and Practical Redux, Part 7: Feature Reducers. I'll paste in a basic hypothetical example:
export function commentsReducer(state = initialState, action, hasPostReallyBeenAdded) {}
// elsewhere
export default function rootReducer(state = initialState, action) {
const postState = postsReducer(state.post, action);
const {hasPostReallyBeenAdded} = postState;
const commentState = commentsReducer(state.comments, action, hasPostReallyBeenAdded);
return { post : postState, comments : commentState };
}
I'm working with Redux and my state is a normalized one with a lot of different models. Now I was wondering myself if it was better to have specific actions like:
{type: CHANGE_MODEL_NAME, modelId, name}
vs
{type: UPDATE_MODEL, modelId, {name}}
I did a bit of searching and I found this question:
Is it ok to create generic redux update action
Now what I'm wondering is that no one is adressing the fact that having specific action types allow for different reducers to 'react' to an action in a cleaner way.
IE: I have a model that is copied from another model like so:
{
name: 'foo',
originalModel: id_0
}
It then becomes easier to react to specific actions in my reducer of copied models if I only want to react to the name change action.
Is it wrong for 2 reducers to react to the same actions? Is that why nobody adressed this issue in the original question?
Having multiple slice reducers respond to the same actions is absolutely an intended use case for Redux. I covered that background in my post The Tao of Redux, Part 1 - Implementation and Intent.
As for your specific question: I think it's entirely valid to have an update action for normalized data that contains the item type name and the item ID. In fact, I demonstrated this exact approach in my post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.
Overall, Redux itself doesn't care what specific action types you have and how generic they are. You are encouraged to define whatever actions are appropriate for your app, and what level of "abstraction" they represent. It's very reasonable to make them a bit more generic - for example, I'd prefer UPDATE_USER_ATTRIBUTES instead of SET_USER_FIRST_NAME and SET_USER_LAST_NAME, but ultimately it's up to you.
This is perfectly valid. This pattern even has a name. "Applying a change set"
Your message becomes the following:
{type: APPLY_CHANGSET, data: {id: idOfThingToApplyTo, propOne: '1', propTwo: '2'}}
Your reducers can then look like this:
const propOneReducer = (value = 'default', {type, {data: {propOne}}) => {
return type === APPLY_CHANGSET && propOne !== undefined ? propOne : value;
}
This makes it a lot easier to add new properties (attributes) to your objects in your store. Adding a reducer, and sending the data from your react views to the actionCreator. In simple cases, you might not even need to change the actionCreator.
In these simple cases you can even build a reducer creator, basically creating the reducer for you.
Pro's
Less actions in the system
Simple sweet actionCreators
Not Pro's
Actions don't describe exactly what is happening. It's also harder to parse exactly what happens to the store after a actionCreator is invoked. This because the reducers now take the shape of the data into account.
Slightly more complex reducers
I'm using Redux to write a NodeJS app. I'm interested in allowing users to dynamically load middleware by specifying it at runtime.
How do I dynamically update the middleware of a running Redux application to add or remove middleware?
Middleware is not some separate extension, it's part of what your store is. Swapping it at runtime could lead to inconsistencies. How do you reason about your actions if you don't know what middleware they'll be run through? (Keep in mind that middlewares don't have to operate synchronously.)
You could try a naive implementation like the following:
const middlewares = [];
const middlewareMiddleware = store => next => act => {
const nextMiddleware = remaining => action => remaining.length
? remaining[0](store)(nextMiddleware(remaining.slice(1)))(action)
: next(action);
nextMiddleware(middlewares)(act);
};
// ... now add/remove middlewares to/from the array at runtime as you wish
but take note of the middleware contract, particularly the next argument. Each middleware receives a "pass to the next middleware" function as part of its construction. Even if you apply middlewares dynamically, you still need to tell them how to pass their result to the next middleware in line. Now you're faced with a loss-loss choice:
action will go through all of the middleware registered at the time it was dispatched (as shown above), even if it was removed or other middleware was added in the meantime, or
each time the action is passed on, it goes to the next currently registered middleware (implementation is a trivial excercise), so it's possible for an action to go through a combination of middlewares that were never registered together at a single point in time.
It might be a good idea to avoid these problems alltogether by sticking to static middleware.
Use redux-dynamic-middlewares
https://github.com/pofigizm/redux-dynamic-middlewares
Attempting to change middleware on-the-fly would violate the principle of 'pure' actions and reducer functions, because it introduces side-effects. The resulting app will be difficult to unit-test.
Off the top of my head, it might be possible to create multiple stores (one for each possible middleware configuration), and use a parent store to provide the state switch between them. You'd move the data between the sub-stores when switching. Caveat: I've not seen this done, and there might be good reasons for not doing it.