We working on a new project with a stack of Node.js in the BE + React and Redux in the FE.
Basically, this project is a "thin" version of an existing project - so it will have some basic features of the existing project + some new features.
I am looking for a way to reduce the copy-paste and duplication of code in those 2 projects(and can be more "duplicate" projects like this in the future).
In the BE we don't have any problem - the BE is the same for all FE projects.
On the other side, we have a UI library that we use - so all the atomic components are reused and we do not have any "copy-paste" here too.
The parts that we found ourselves copy-paste between multiple projects are all the "Redux" part - reducers, selectors, actions, etc.
There are any best practices/libraries/design patterns that we can follow to write the "Redux" parts once and use them in multiple projects?
The way I implement this is to have reusable logic hold its state as a separate branch in the store. So for example:
const state = {
user: defaultUserState,
messages: defaultMessagingState,
search: defaultSearchState
}
The reducers are similarly implemented in separate sub-components and combined:
combineReducers({
user: userReducer,
messages: messagingReducer,
search: searchReducer
})
I use redux-logic which is implemented as middleware. I am expecting to be able to do the same thing with the side effects.
The current leakage is related to navigation - which feature components are shown depending on which actions have been fired is defined in the main next project in the top level navigation reducer.
Related
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 trying to get how to add ngrx into my current project structure.
I've got all concepts around ngrx/reflux. Nevertheless, I don't quite figure out how to rebuild project structure in order to integrate it into my project.
Where do I need to add reducers?
Where do I need to add states?
What about actions?
Where should Store be, on a service, injected in each component?
Where or when the data should be fetched from server?
Is there any project structure best practice over there?
First, you should take a look into #ngrx/store documentation : https://github.com/ngrx/store#setup
I've made a small (funny) project to demonstrate how to use :
- angular
- ngrx/store
- ngrx/effects
- normalized state
- selectors
You can find it here : https://github.com/maxime1992/pizza-sync
To give you some info about how it works :
- core.module.ts is were I declare my root reducer
- root.reducer.ts is were I build the root reducer and compose it with middlewares according to dev/prod env
- For a reducer, I keep every related part together (interface(s) + reducer + effects + selectors)
Then within a component, to access the store simply do like that :
Inject the store :
constructor(private _store$: Store<IStore>) { }
Get data from either
a) A selector (ex)
this._pizzasCategories$ = this._store$.let(getCategoriesAndPizzas());
b) Directly from the store (ex)
this._idCurrentUser$ = this
._store$
.select(state => state.users.idCurrentUser);
Notice that I didn't subscribe which means I'm using in my view the async pipe so angular subscribe to the Observable for me.
But of course you can also do it by hand and use subscribe in your ts.
PS: I'll be releasing a starter with all that already setup to avoid loosing time with all of that when starting a new project. I'll try to release it this week and I'll update this post as soon as it's done. Maybe it might help you.
EDIT 12/05/17
Late but I finally released the starter :) !
https://github.com/maxime1992/angular-ngrx-starter
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