Redux: Why not put actions and reducer in same file? - javascript

I'm creating an app with Redux and am scratching my head as to why it is best to place actions and reducers in separate files. At least, that's the impression I'm getting from all the examples.
Each action, or action creator, appears to map to a single function that is called by a reducer (inside a switch statement). Wouldn't it be logical to keep these together in the same file? It also makes using the same constant for the action type and switch case easier, as it doesn't have to be exported/imported between files.

From Redux creator Dan Abramov:
Many reducers may handle one action. One reducer may handle many
actions. Putting them together negates many benefits of how Flux and
Redux application scale. This leads to code bloat and unnecessary
coupling. You lose the flexibility of reacting to the same action from
different places, and your action creators start to act like
“setters”, coupled to a specific state shape, thus coupling the
components to it as well.
From the Redux docs:
We suggest you write independent small reducer functions that are each
responsible for updates to a specific slice of state. We call this
pattern “reducer composition”. A given action could be handled by all,
some, or none of them. This keep components decoupled from the actual
data changes, as one action may affect different parts of the state
tree, and there is no need for the component to be aware of this.
See this conversation on twitter and this issue on github for more information.

Keeping Actions and Reducers in separate files helps keep the code modular.
It can be easier to find bugs, extend the code, and in general work on the smallest piece possible.
Example:
Saving API error messages to the Redux store can be helpful.
If I forgot to update the store with the incoming error on one of the Reducers, that could be tough to find across multiple files.
If I'm looking at multiple Reducers in the same file, it'll be easier to see that one of them is missing the error: action.payload line.

Related

Redux: Generic Reducer vs Reducer per Entity

When using Redux, which is generally better practice: To implement entity specific reducers or to implement generic reducer based on strict action keying conventions? Former solution has a lot of boilerplate code but is loosely coupled and more flexible. Latter solution has less of boilerplate but is more fragile and restrictive.
As our domain has dozens of entities, I initially leaned towards latter solution. I designed generic entity reducer which utilizes JavaScript computed property names to manipulate the state according to dispatched action types. It's all dynamic and convention based.
At first the solution worked well, until I realized there's a structural mismatch among different entity resources in some use cases. Some endpoints returns a collection, where as some returns a singe result and so on. Thus I had to decompose the generic reducer to adapt to different use cases. I started adding snippets of conditional logic here and there. And eventually found myself lost and confused! Now I have hard time getting things back in order. Architecturally lucid vision actually ended up as hard to maintain big ball of mud very quickly.
Should I just give up with generic solution and refactor the store to use entity specific reducers regardless of boilerplate? Has someone actually implemented and maintained generic reducer logic for complex Admin GUI application successfully?
At first the solution worked well, until I realized there's a
structural mismatch among different entity resources in some use
cases. Some endpoints returns a collection, where as some returns a
singe result and so on. Thus I had to decompose the generic reducer to
adapt to different use cases.
As you have correctly pointed out that generic solution get very complex very quickly with lot of conditionals and subsequent state modification.
Having a generic reducer is okay if we have small static website with certain actions.
But if you have dynamic website with huge number of entities or nested entities(scary), shape of the state can get very complex. In my opinion, handing that kind of shape in 1 reducer is not a prudent approach.
Having modular reducers also completely decouples multiple action dispatches and state changes that may happen with single action of the user.
#Kos wrote a fabulous answer here which you may want to read.

How can I tell Redux not to update React state yet?

I am currently dealing with a very large table of data. Some actions the user can take are quite complex - complex enough that I might end up dispatching a hundred actual actions to Redux, many of which will depend on the state being updated by previous actions, and causing a hundred state changes... which will result in the page seeming to lock up as numerous things in a very large table are rendered a hundred times, even though none of these updates will be individually meaningful to the user (or actively confusing)
Is there a way to delay Redux/React from seeing these changes - to say "okay, don't bother pestering React about this stuff, don't recalculate any props, don't do anything but throw this stuff through the reducers until it's done and I tell you it's done, and then return to normal behaviour" ?
I know I could set some React state property and then have a shouldUpdateComponent in each of my many components, but I was hoping there was a solution that involved less duplicate code scattered across dozens of files and perhaps even a bit more efficiency of avoiding calling the same exact function dozens of times per update.
Any suggestions?
Dan Abramov himself wrote on twitter an example of how to do this using batched actions and a higher order reducer.
https://twitter.com/dan_abramov/status/656074974533459968?lang=en
The gist of the idea is to wrap the actions you want to batch in another action, and define a higher order reducer (a reducer that returns another reducer, eg. redux-undo) that will apply all these actions when it handles the batched action update.

Redux - how is state passed to reducers?

I'm just wrapping my head around Redux and I just wanted to run a question by on how state is passed to reducers:
I understand that the structure of state is dictated by the combineReducers() function and that the keys that you define within this function will become the various properties of state.
Am I correct in saying that only the subset of state that is associated with a particular reducer is passed to the reducer? Or is the entire state object passed?
Having lots of 'ahahhh' moments with Redux...!
combineReducers takes a number of reducers as parameters and returns a reducer. The particular reducer created by combineReducers only passes the part of the state assigned to a particular reducer. This way each reducer is completely in charge of its own part of the state and nothing more.
But you can write your own kind of combineReducers that could work differently. This is just to say that there is nothing special about combineReducers.
A reducer is just a function that takes a state and an action and returns the state with the action applied in some way. Or not, at its discretion.
For instance, in Redux it is common for separate branches of the state to handle the same (set of) actions but handle them in different ways that make sense for that part of the state. Each action is passed to all sub-reducers by combineReducers, giving each branch's reducers a chance to either do something, or nothing.
So in most common usage, a reducer is solely responsible for a slice of the state and sub-reducers, such as those passed to combineReducers, or reducers called by your own reducers, are solely responsible for their sub-state/branch.
But your own reducers may call a sub-reducer and pass it anything you like, such as the same state that was already passed to some other reducer, or even some data it created without it ever having been available in the global state.
These things are not common, but if you see a need, and feel confident to go forward with it, you are encouraged to use what you believe works best. Great power comes with great responsibility.
combineReducers is just a helper function for a common use case. It's probably cleanest to make a single reducer responsible for a particular branch of the state, but there may be situations where you find it beneficial to do things differently. A single reducer can manage a complex object with nesting without calling any other reducer, but a common pattern is to make smaller reducers to handle parts, if the parent is getting unwieldy.
The main point is to realize that a reducer is always a pure function that takes state and an action and returns a state. How you make this happen is entirely up to you. Most developers choose to follow common patterns seen in other's code, such as Dan's Redux examples or the videos he has on egghead.io.
Treading on the beaten path is often a good way to start out because it has already been vetted.
By the same token, however, it is always important to know why something is done. Developers sometimes make things more complex than it needs to be because of following an example that either was meant for something else or wasn't thought through very well to begin with.
Also read this article by Dan.

When should I add Redux to a React app?

I'm currently learning React and I am trying to figure out how to use it with Redux for building a mobile app. I'm kind of confused on how the two are related/usable together. For example, I completed this tutorial in React https://www.raywenderlich.com/99473/introducing-react-native-building-apps-javascript, but now I want to play around with adding some reducers/actions to that app and I am not sure where those would tie in with what I've already done.
React is a UI framework that takes care of updating the UI in response to the “source of truth” that is usually described as a state “owned” by some component. Thinking in React describes the React state ownership concept very well, and I strongly suggest you go through it.
This state ownership model works well when the state is hierarchical and more or less matches the component structure. This way the state gets “spread out” across many components, and the app is easy to understand.
However sometimes distant parts of the app want to have access to the same state, for example, if you cache fetched data and want to consistently update it everywhere at the same time. In this case, if you follow the React model, you’ll end up with a bunch of very large components at the top of the component tree that pass a myriad of props down through some intermediate components that don’t use them, just to reach a few leaf components that actually care about that data.
When you find yourself in this situation, you can (but don’t have to) use Redux to “extract” this state management logic from the top-level components into separate functions called “reducers”, and “connect” the leaf components that care about that state directly to it instead of passing the props through the whole app. If you don’t have this problem yet, you probably don’t need Redux.
Finally, note that Redux is not a definitive solution to this problem. There are many other ways to manage your local state outside the React components—for example, some people who didn’t like Redux are happy with MobX. I would suggest you to first get a firm understanding of React state model, and then evaluate different solutions independently, and build small apps with them to get a sense of their strengths and weaknesses.
(This answer is inspired by Pete Hunt’s react-howto guide, I suggest you to read it as well.)
I've found that the ideal path for adding Redux to an application/stack is to wait until after you/app/team are feeling the pains that it solves. Once you start seeing long chains of props building up and being passed down through multiple levels of components or your finding yourself orchestrating complex state manipulations/reads, that could be a sign that your app may benefit from introducing Redux et al.
I recommend taking an app that you've already built with "just React" and see how Redux might fit into it. See if you can gracefully introduce it by plucking out one piece of state or set of "actions" at a time. Refactor towards it, without getting hung up on a big bang rewrite of your app. If you're still having trouble seeing where it might add value, then that could be a sign that your app is either not large or complex enough to merit something like Redux on top of React.
If you haven't come across it yet, Dan (answered above) has a great short-video series that walks through Redux on a more fundamental level. I highly suggest spending some time absorbing pieces of it: https://egghead.io/series/getting-started-with-redux
Redux also has some pretty great docs. Especially explaining a lot of the "why" such as http://redux.js.org/docs/introduction/ThreePrinciples.html
I have prepared this document to understand Redux. Hope this clears your doubt.
-------------------------- REDUX TUTORIAL ----------------------
ACTIONS-
Actions are payloads of information that send data from your application to the store. They are the only source of information from the store. You can send them
only using store.dispatch().
Example-
const ADD_TODO = 'ADD_TODO'
{
type:ADD_TODO,
text: 'Build my first redux app'
}
Actions are plain javascript object. Action must have a [ type ] property that indicates the type of action being performed. The type should be defined as a string constant.
ACTION CREAToRS-----
---------------- ---- Action creators are exactly the function that creates action
It is easy to conflate the terms - action and action creator.
In redux action, creator returns an action.
function addToDo(text) {
return {
type: ADD_TODO,
text
}
}
to initialte dispatch pass the result to the dispatch() function.
dispatch(addToDo(text));
dispatch(completeToDo(index))
Alternatively, you can create a bound action creator that automatically dispatches.
cosnt boundAddTodO = text => dispatch(addToDo(text));
now you can directly called it
boundaddTodO(text);
The dispatch() functionn can be directly accessed from store.dispatch(). but we
access it using a helper connect() method.
Actions.js.....................
Actions...........
exports cosnt ADD_TODO = 'ADD_TODO';
exports cosnt TOGGLE_TODO = 'TOGGLE_TODO'
Actions Creators
export function addToDO(text){
return {
type: ADD_TODO,
text
}
}
.........................REDUCERS..................................
Reducers specify how the applications state changes in response to actions sent to the store.
Designing the state shap
In redux all the application state is store in single object. You have to store some data as well as some state.
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using redux',
completed: true
},
{
text: 'Kepp all the state in single tree'
}
]
}
Handling Actions
---------------- the reducers are the pure functions that take the previous state and action, and return a new state.
(previousState, action) => newState
We will start by specifying the initial state. Redux will call our reducers with an undefined state for the first time. this is our chance to return the state of our app.
import { visiblilityFilters } from './actions';
const initialState = {
visibilityFilter: VisibilityFilters.SHOW_ALL,
todo: []
}
function todoApp(state, action){
if(typeof state == 'undefined'){
return initialState;
}
// dont handle other cases as of now.
return state;
}
you can do the same using ES6 way of handling the JS
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
................................. STORE...................................
The store is a object that brings them together. the store has following responsbility
hold application state
allow access to state via getState()
Allow state to be updated via dispatch()
Register listerneres via suscriber(listener)
Note. use combineReducers() to combine several reducers into one.
const store = createStore(todoapp); // the todoapp are the reducers
This is how redux works. A action is dispatched from any compoenent or view. Action MUST have "type" property and may be any property which holds information of action happened. The data passed in action, could be relevant to different reducer, so same object get passed to different reducer. Each reducer takes/ make-out its part/contribution to state. The output is then merged and new state get formed, and the component which must be subscribed for state change event gets notified.
In above example, brown color has all 3 component RGB. Each reducer receives same brown color and they seperate out its contribution to the color.
Firstly, you don't need to add Redux to your application if you don't need it! Simple, so don't force yourself to include it in your project if you don't need it at all! But that doesn't mean Redux is no good, it's really helpful in large applications, so read on ...
Redux is a state management for your React application, think about Redux like a local store which track of your state as you go, you can access the state in any page and route you want, also compare to Flux, you only have one store, means one source of truth...
Look at this image to understand what Redux does first at a glance:
Also this's how Redux introduce itself:
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in
different environments (client, server, and native), and are easy to
test. On top of that, it provides a great developer experience, such
as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library.
It is tiny (2kB, including dependencies).
Also as per documentation, there are Three Principles for Redux as below:
1. Single source of truth
2. State is read-only
3. Changes are made with pure functions
So basically when you need to a single store to keep track of anything you like in your application, then Redux is handy, you can access it anywhere in your app, in any route... simply using store.getState();
Also using the middleware Redux, you can do manage the state much better, there a list of handy components and middleware on official page of Redux!
Simply if your application gonna be big, with many components, states and routing try to implements Redux from start! It will help you on the way for sure!
When we write application we need to manage state of the application.
The React manages states locally within the component if we need to share the states between components we can use props or callbacks.
But as application grows it becomes difficult to manage states and state transformations.State and state transformations need to properly tracked in order to debug the applications.
Redux is a predictable state container for JavaScript apps that manages state and state transformations and is often used with React,
The concept of redux can be explained in following image.
When user triggers an action when user interact with the component and an action is dispatched to store then the reducer in the store accepts the action and update the state of the application and stored in the application wide immutable global variable when there is an update in store the corresponding view component subscribed to the state will get updated.
Since state is managed globally and with redux it is easier to maintain.
Having used Redux and personally finding it cumbersome, I found passing around an object to my components as a prop can be a much easier way to maintain state. Not to mention it's an easy way of making references to functions to call in other components. It can solve a lot of the cumbersome issues of passing messages between components in React, so it's a two for one.

Flux: common practices to separate actions btw action creators

If you'll look at flux-chat example you may see three action creators. Pretty much for such small hello-world app. I'm writting React+Flux application and wondering what is the common practice for bigger apps to separate actions between action creators? Should I create the only one or create separate for each module? If I will create the separate for each module - than they will depend among each others, isthat acceptable?
Action creators should all be on the same level of abstraction, meaning they should never depend on each other (notice that none of them depend on each other here: https://github.com/facebook/flux/tree/master/examples/flux-chat/js/actions). And while action creators shouldn't depend on each other, they can depend on multiple stores, and multiple actions can depend on the same store. This is what separates actions from action creators since the actions are really defined inside of a store. So if you need to access data from more than one store, you can do that in an action creator (They use the thread store in the message store, and that's bad practice. The thread data should be passed into the action rather than included via the store).
In the flux chat example, they have another concept called Utils (very general) which doesn't emit events or do anything to the application state. In fact, it doesn't really add to the flux architecture. It is just a concept born out of the DRY principle.
EDIT:
The reason why action creators depend on stores is because it is a separation in abstraction that specifies where logic belongs. As an analogy, you wouldn't be worrying about allocating memory when working on a function that is meant for addition of two integers. You want to only have the relevant code pertaining to the calculation, and delegate low level details to other methods. This is the same difference in abstraction that the stores and actions are providing. Actions are working to connect separate services/stores together, while stores maintain how to interact with http, websockets, and application state.

Categories

Resources