Redux middleware change state before next() - javascript

I would like to modify the state before next() is called so every reducer applied after the middleware gets the new state. Is it possible? How?
The only idea that comes to my mind is very hacky and would be something like this:
export const myMiddleware = (store) => (next) => (action) => {
const oldReducer = ????
store.replaceReducer(myReducer);
store.dispatch(action);
const newState = store.getState();
store.replaceReducer(oldReducer);
return next(newState);
}
As I haven't seen any method to get the current reducer, it should be given to the middleware in any manner:
export const myMiddleware = (oldReducer) => (store) => (next) => (action) => {
...
}
const store = createStore(originalReducer, applyMiddleware(myMiddleware(originalReducer)));
Which seems even more hacky!
The main purpose is to build a package that maps an action object (action.payload) and a path (action.meta) in store state. In this scenario, the reducer is distributed in an npm package, so it should be "chained" somehow. So right now the reducer is detecting if there is a path and an object inside payload, and tries to reduce the new state from it.
The worst solution is to instruct the user to call the reducer from their own reducer, just before any other action inside the reducer. This is not a solid pattern. So at first, I was thinking in an as much agnostic as possible middleware that automatically does the work. And that's why I'm trying to modify state from middleware if possible.

You probably don't want to be calling individual reducers from within middleware. It sounds like you're condensing what should be multiple sequential actions into a single action, which is what's causing you problems. If you use something like redux-saga to manage chains of actions, you can likely accomplish what you're looking for pretty easily.
Here's a basic example of managing a sequence of actions with redux-saga:
import { takeEvery } from 'redux-saga'
import { put } from 'redux-saga/effects'
export function * watchForSomeAction () {
// Every time SOME_ACTION is dispatched, doSomethingElse() will be called
// with the action as its argument
yield * takeEvery('SOME_ACTION', doSomethingElse)
}
export function * doSomethingElse (action) {
// put() is redux-saga's way of dispatching actions
yield put({ type: 'ANOTHER_ACTION', payload: action.payload })
}
This example simply watches for SOME_ACTION, and when it happens, it dispatches ANOTHER_ACTION. With something like this, you can ensure that ANOTHER_ACTION's reducers are dealing with the new state resulting from SOME_ACTION's reducers.

Related

simplify redux with generic action & reducer

In React-Redux project, people usually create multiple actions & reducers for each connected component. However, this creates a lot of code for simple data updates.
Is it a good practice to use a single generic action & reducer to encapsulate all data changes, in order to simplify and fasten app development.
What would be the disadvantages or performance loss using this method. Because I see no significant tradeoff, and it makes development much easier, and we can put all of them in a single file! Example of such architecture:
// Say we're in user.js, User page
// state
var initialState = {};
// generic action --> we only need to write ONE DISPATCHER
function setState(obj){
Store.dispatch({ type: 'SET_USER', data: obj });
}
// generic reducer --> we only need to write ONE ACTION REDUCER
function userReducer = function(state = initialState, action){
switch (action.type) {
case 'SET_USER': return { ...state, ...action.data };
default: return state;
}
};
// define component
var User = React.createClass({
render: function(){
// Here's the magic...
// We can just call the generic setState() to update any data.
// No need to create separate dispatchers and reducers,
// thus greatly simplifying and fasten app development.
return [
<div onClick={() => setState({ someField: 1 })}/>,
<div onClick={() => setState({ someOtherField: 2, randomField: 3 })}/>,
<div onClick={() => setState({ orJustAnything: [1,2,3] })}/>
]
}
});
// register component for data update
function mapStateToProps(state){
return { ...state.user };
}
export default connect(mapStateToProps)(User);
Edit
So the typical Redux architecture suggests creating:
Centralized files with all the actions
Centralized files with all the reducers
Question is, why a 2-step process? Here's another architectural suggestion:
Create 1 set of files containing all the setXField() that handle all the data changes. And other components simply use them to trigger changes. Easy. Example:
/** UserAPI.js
* Containing all methods for User.
* Other components can just call them.
*/
// state
var initialState = {};
// generic action
function setState(obj){
Store.dispatch({ type: 'SET_USER', data: obj });
}
// generic reducer
function userReducer = function(state = initialState, action){
switch (action.type) {
case 'SET_USER': return { ...state, ...action.data };
default: return state;
}
};
// API that we export
let UserAPI = {};
// set user name
UserAPI.setName = function(name){
$.post('/user/name', { name }, function({ ajaxSuccess }){
if (ajaxSuccess) setState({ name });
});
};
// set user picture URL
UserAPI.setPicture = function(url){
$.post('/user/picture', { url }, function({ ajaxSuccess }){
if (ajaxSuccess) setState({ url });
});
};
// logout, clear user
UserAPI.logout = function(){
$.post('/logout', {}, function(){
setState(initialState);
});
};
// Etc, you got the idea...
// Moreover, you can add a bunch of other User related methods,
// like some helper methods unrelated to Redux, or Ajax getters.
// Now you have everything related to User available in a single file!
// It becomes much easier to read through and understand.
// Finally, you can export a single UserAPI object, so other
// components only need to import it once.
export default UserAPI
Please read through the comments in the code section above.
Now instead of having a bunch of actions/dispatchers/reducers. You have 1 file encapsulating everything needed for the User concept. Why is it a bad practice? IMO, it makes programmer's life much easier, and other programmers can just read through the file from top to bottom to understand the business logic, they don't need to switch back and forth between action/reducer files. Heck, even redux-thunk isn't needed! And you can even test the functions one by one as well. So testability is not lost.
Firstly, instead of calling store.dispatch in your action creator, it should return an object (action) instead, which simplifies testing and enables server rendering.
const setState = (obj) => ({
type: 'SET_USER',
data: obj
})
onClick={() => this.props.setState(...)}
// bind the action creator to the dispatcher
connect(mapStateToProps, { setState })(User)
You should also use ES6 class instead of React.createClass.
Back to the topic, a more specialised action creator would be something like:
const setSomeField = value => ({
type: 'SET_SOME_FIELD',
value,
});
...
case 'SET_SOME_FIELD':
return { ...state, someField: action.value };
Advantages of this approach over your generic one
1. Higher reusability
If someField is set in multiple places, it's cleaner to call setSomeField(someValue) than setState({ someField: someValue })}.
2. Higher testability
You can easily test setSomeField to make sure it's correctly altering only the related state.
With the generic setState, you could test for setState({ someField: someValue })} too, but there's no direct guarantee that all your code will call it correctly.
Eg. someone in your team might make a typo and call setState({ someFeild: someValue })} instead.
Conclusion
The disadvantages are not exactly significant, so it's perfectly fine to use the generic action creator to reduce the number of specialised action creators if you believe it's worth the trade-off for your project.
EDIT
Regarding your suggestion to put reducers and actions in the same file: generally it's preferred to keep them in separate files for modularity; this is a general principle that is not unique to React.
You can however put related reducer and action files in the same folder, which might be better/worse depending on your project requirements. See this and this for some background.
You would also need to export userReducer for your root reducer, unless you are using multiple stores which is generally not recommended.
I mostly use redux to cache API responses mostly, here are few cases where i thought it is limited.
1) What if i'm calling different API's which has the same KEY but goes to a different Object?
2) How can I take care if the data is a stream from a socket ? Do i need to iterate the object to get the type(as the type will be in the header and response in the payload) or ask my backend resource to send it with a certain schema.
3) This also fails for api's if we are using some third party vendor where we have no control of the output we get.
It's always good to have control on what data going where.In apps which are very big something like a network monitoring application we might end up overwriting the data if we have same KEY and JavaScript being loosed typed may end this to a lot weird way this only works for few cases where we have complete control on the data which is very few some thing like this application.
Okay i'm just gonna write my own answer:
when using redux ask yourself these two questions:
Do I need access to the data across multiple components?
Are those components on a different node tree? What I mean is it isn't a child component.
If your answer is yes then use redux for these data as you can easily pass those data to your components via connect() API which in term makes them containers.
At times if you find yourself the need to pass data to a parent component, then you need to reconsider where your state lives. There is a thing called Lifting the State Up.
If your data only matters to your component, then you should only use setState to keep your scope tight. Example:
class MyComponent extends Component {
constructor() {
super()
this.state={ name: 'anonymous' }
}
render() {
const { name } = this.state
return (<div>
My name is { name }.
<button onClick={()=>this.setState({ name: 'John Doe' })}>show name</button>
</div>)
}
}
Also remember to maintain unidirectional data flow of data. Don't just connect a component to redux store if in the first place the data is already accessible by its parent component like this:
<ChildComponent yourdata={yourdata} />
If you need to change a parent's state from a child just pass the context of a function to the logic of your child component. Example:
In parent component
updateName(name) {
this.setState({ name })
}
render() {
return(<div><ChildComponent onChange={::this.updateName} /></div>)
}
In child component
<button onClick={()=>this.props.onChange('John Doe')}
Here is a good article about this.
Just practice and everything will start to make sense once you know how to properly abstract your app to separate concerns. On these matter composition vs ihhertitance and thinking in react are a very good read.
I started writing a package to make it easier and more generic. Also to improve performance. It's still in its early stages (38% coverage). Here's a little snippet (if you can use new ES6 features) however there is also alternatives.
import { create_store } from 'redux';
import { create_reducer, redup } from 'redux-decorator';
class State {
#redup("Todos", "AddTodo", [])
addTodo(state, action) {
return [...state, { id: 2 }];
}
#redup("Todos", "RemoveTodo", [])
removeTodo(state, action) {
console.log("running remove todo");
const copy = [...state];
copy.splice(action.index, 1);
return copy;
}
}
const store = createStore(create_reducer(new State()));
You can also even nest your state:
class Note{
#redup("Notes","AddNote",[])
addNote(state,action){
//Code to add a note
}
}
class State{
aConstant = 1
#redup("Todos","AddTodo",[])
addTodo(state,action){
//Code to add a todo
}
note = new Note();
}
// create store...
//Adds a note
store.dispatch({
type:'AddNote'
})
//Log notes
console.log(store.getState().note.Notes)
Lots of documentation available on NPM. As always, feel free to contribute!
A key decision to be made when designing React/Redux programs is where to put business logic (it has to go somewhere!).
It could go in the React components, in the action creators, in the reducers, or a combination of those. Whether the generic action/reducer combination is sensible depends on where the business logic goes.
If the React components do the majority of the business logic, then the action creators and reducers can be very lightweight, and could be put into a single file as you suggest, without any problems, except making the React components more complex.
The reason that most React/Redux projects seem to have a lot of files for action creators and reducers because some of the business logic is put in there, and so would result in a very bloated file, if the generic method was used.
Personally, I prefer to have very simple reducers and simple components, and have a large number of actions to abstract away complexity like requesting data from a web service into the action creators, but the "right" way depends on the project at hand.
A quick note: As mentioned in https://stackoverflow.com/a/50646935, the object should be returned from setState. This is because some asynchronous processing may need to happen before store.dispatch is called.
An example of reducing boilerplate is below. Here, a generic reducer is used, which reduces code needed, but is only possible the logic is handled elsewhere so that actions are made as simple as possible.
import ActionType from "../actionsEnum.jsx";
const reducer = (state = {
// Initial state ...
}, action) => {
var actionsAllowed = Object.keys(ActionType).map(key => {
return ActionType[key];
});
if (actionsAllowed.includes(action.type) && action.type !== ActionType.NOP) {
return makeNewState(state, action.state);
} else {
return state;
}
}
const makeNewState = (oldState, partialState) => {
var newState = Object.assign({}, oldState);
const values = Object.values(partialState);
Object.keys(partialState).forEach((key, ind) => {
newState[key] = values[ind];
});
return newState;
};
export default reducer;
tldr It is a design decision to be made early on in development because it affects how a large portion of the program is structured.
Performance wise not much. But from a design perspective quite a few. By having multiple reducers you can have separation of concerns - each module only concerned with themselves. By having action creators you add a layer of indirection -allowing you to make changes more easily. In the end it still depends, if you don't need these features a generic solution helps reduce code.
First of all, some terminology:
action: a message that we want to dispatch to all reducers. It can be anything. Usually it's a simple Javascript object like const someAction = {type: 'SOME_ACTION', payload: [1, 2, 3]}
action type: a constant used by the action creators to build an action, and by the reducers to understand which action they have just received. You use them to avoid typing 'SOME_ACTION' both in the action creators and in the reducers. You define an action type like const SOME_ACTION = 'SOME_ACTION' so you can import it in the action creators and in the reducers.
action creator: a function that creates an action and dispatches it to the reducers.
reducer: a function that receives all actions dispatched to the store, and it's responsible for updating the state for that redux store (you might have multiple stores if your application is complex).
Now, to the question.
I think that a generic action creator is not a great idea.
Your application might need to use the following action creators:
fetchData()
fetchUser(id)
fetchCity(lat, lon)
Implementing the logic of dealing with a different number of arguments in a single action creator doesn't sound right to me.
I think it's much better to have many small functions because they have different responsibilities. For instance, fetchUser should not have anything to do with fetchCity.
I start out by creating a module for all of my action types and action creators. If my application grows, I might separate the action creators into different modules (e.g. actions/user.js, actions/cities.js), but I think that having separate module/s for action types is a bit overkill.
As for the reducers, I think that a single reducer is a viable option if you don't have to deal with too many actions.
A reducer receives all the actions dispatched by the action creators. Then, by looking at the action.type, it creates a new state of the store. Since you have to deal with all the incoming actions anyway, I find it nice to have all the logic in one place. This of course starts to be difficult if your application grows (e.g. a switch/case to handle 20 different actions is not very maintainable).
You can start with a single reducer, the move to several reducers and combine them in a root reducer with the combineReducer function.

Final state after dispatching action is empty while testing redux-mock-store

I'm using redux-mock-store for testing my redux actions. However, I want to test my actions and the store in which they're getting set. My code goes something like this:
import configureStore from 'redux-mock-store'
import thunk from 'redux-thunk'
const mockStore = configureStore([thunk])
const store = mockStore({ ...state })
console.log(store.getState()) // => { ...state }
store.dispatch(someAction())
console.log(store.getActions()) // => [ someAction() ] The action does show up here!
console.log(store.getState()) // => { ...state } But this is the same unchanged state as above
But my finalState is empty, I understand that no reducer is listening my action and the state is not getting updated. But I want to test my state with actions at one place. Is there any way to integrate that or there are some other npm module which provides that?
Edit:
I want to test my store and actions at one place. Is there or is there not a way to do that? Should I or should I not do that?
From the doc of redux-mock-store:
Please note that this library is designed to test the action-related logic, not the reducer-related one. In other words, it does not update the Redux store. If you want a complex test combining actions and reducers together, take a look at other libraries (e.g., redux-actions-assertions). Refer to issue #71
for more details.

redux-saga injected twice

I have a redux-saga which is called once, but it is executing twice.
This is the action that starts the saga:
export function createRequest (data) {
return {
type: CREATE_REQUEST,
payload: {data}
};
}
and my sagas.js file looks this way:
export function* create (x) {
try {
const response = yield call(request, URL_TO_API, Object.assign({}, buildBaseHeaders('en'), {
method: 'post',
body: JSON.stringify(x.payload.data)
}));
yield put(createSuccess(response));
} catch (error) {
yield put(createFailure(error));
}
}
... my other sagas
export default function* defaultSaga () {
yield takeLatest(CREATE_REQUEST, create);
... my other calls
}
The way I'm injecting the sagas into my React component is this:
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({key: 'myComponent', reducer});
const withSaga = injectSaga({key: 'myComponent', saga});
export default compose(withReducer, withSaga, withConnect) MyComponent;
But the saga is injected twice. So, what am I missing here? How can I inject the saga only once no matter on how many times MyComponent gets rendered?
But the saga is injected twice. So, what am I missing here?
Solution is dependent on how redux-sagas-injector npm library works. In general case, asynchronous loading and applying for sagas is difficult thing, because saga is consists of "live" process manager, which can not be disposed on some function call or object deletion.
It implies from saga's ability to launch custom tick callback domains (Promises, AJAX/XHR, setImmediate, etc), which can not be disposed from custom external code (That's also reason, why HMR does not work with sagas in partial mode and should reload whole page).
So, if you perform saga injection on router switching, check two things: that old saga has implicit action to dispose from outer side, like special inner technical dispose action, and that there is not misconfiguration in router side - maybe same page had been launched, for example, twice.

Handle async actions that do not store result in state

I stumbled upon a requirement on a section of a vanilla JS webapp that requires a single JSON "definitions" object to render. Definitions are loaded via an HTTP request at the very beginning, read, parsed and handed down to another layer of the app. The object itself never changes throughout its life cycle.
I'm now trying to model this scenario in ReactJS, using Redux + redux-thunk. I created a thunk/async action the fetches the JSON object, extracts what it needs and ends up updating the state with that -
but it does not store the object itself in the state. This seems like the right, logical approach since, as mentioned, the definitions are never modified in any way. I'd argue it's simply not state, in a strict sense.
However, by taking that decision I ended up struggling while implementing the actual React.Component. Almost every single example I've seen out there in the wild for async cases like this one:
Defines a thunk action that fires some API call.
Stores whatever they got back (or after some alterations) in a state property.
Maps that property to this.props in the Component with mapStateToProps and connect.
In my case, I don't really have a state property to bind to. So I ended up returning the definitions object in my async action and using the component's local state to get what I needed.
class ContainerComponent extends React.Component {
state = { definitions: {} };
componentDidMount() {
const { dispatch } = this.props;
dispatch(fetchDefinitions())
.then((definitions) => this.setState({ definitions }));
}
render() {
return (<PresentationalComponent definitions={this.state.definitions} />);
}
}
export default connect()(ContainerComponent);
Not saying that this.setState should be avoided, but this looks an awful lot like what I had before even introducing Redux: an API call returning a promise - only with a lot more meddling indirections.
componentDidMount() {
const { dispatch } = this.props;
fetch(`${API_URL}/definitions`)
.then((res) => res.json())
.then((definitions) => this.setState({ definitions }));
}
So, how should I go about this? Is there any particular thing I am missing here? Any pattern I should be following? Perhaps, avoiding Redux entirely for this matter?
You are right in that having a component state isn't necessarily a bad thing, but I believe you are confused on where to store that data once the API call is made.
You mention that it is not necessarily state, but I would argue otherwise. Prior to making the API call, your application does not have that data. You may have certain UX/UI indications at the start up of your application that, for example could indicate on if the data is being fetched: definitions.all.isFetching.
In your componentDidMount, dispatching the action to fetch the data is correct. Once the action is fired, and the success response is received, your reducer should save the definitions to your redux store like
import { assign, get } from 'lodash';
const all = (
state = { isFetching: false, data: [] },
action,
) => {
switch (action.type) {
case types.LIST_DEFINITIONS:
return assign({}, state, { isFetching: true });
case types.LIST_DEFINITIONS_SUCCESS:
return assign({}, state, get(action, 'result.data'), { isFetching: false });
default: return state;
}
};
Then in your component, you would connect your redux store
function mapStateToProps(state){
return {
definitions: state.definitions.all.data
};
}
export default connect(mapStateToProps, { listDefinitions })(ContainerComponent);
Also note I moved the action out in my example and am placing it into the connect with mapDispatchToProps shorthand.

React + Redux, How to render not after each dispatch, but after several?

I am trying to make multiple changes to the store, but not render till all changes are done. I wanted to do this with redux-thunk.
Here is my action creator:
function addProp(name, value) {
return { type:'ADD_PROP', name, value }
}
function multiGeoChanges(...changes) {
// my goal here is to make multiple changes to geo, and make sure that react doesnt update the render till the end
return async function(dispatch, getState) {
for (let change of changes) {
dispatch(change);
await promiseTimeout(2000);
}
}
}
I dispatch my async action creator like this:
store.dispatch(multiGeoChanges(addProp(1, "val1"), addProp(2, "val2"), addProp(3, "val3")));
However this is causing react to render after each dispatch. I am new to redux-thunk, I never used async middleware, but I thought it could help me here.
#Kokovin Vladislav's answer is correct. To add some additional context:
Redux will notify all subscribers after every dispatch. To cut down on re-renders, either dispatch fewer times, or use one of several approaches for "batching" dispatches and notifications. For more info, see the Redux FAQ on update events: http://redux.js.org/docs/faq/Performance.html#performance-update-events .
I also recently wrote a couple of blog posts that relate to this topic. Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability discusses the pros and cons of using thunks, and summarizes several ways to handle batching of dispatches. Practical Redux Part 6: Connected Lists, Forms, and Performance describes several key aspects to be aware of regarding Redux performance.
Finally, there's several other libraries that can help with batching up store change notifications. See the Store#Store Change Subscriptions section of my Redux addons catalog for a list of relevant addons. In particular, you might be interested in https://github.com/manaflair/redux-batch , which will allow you to dispatch an array of actions with only a single notification event.
There are ways to achieve the goal:
Classic way:
usually:
Actions describe the fact that something happened, but don't specify how the application's state changes in response. This is the job of reducers.
That also means that actions are not setters.
Thus, you could describe what has happened and accumulate changes, and dispatch one action
something like:
const multipleAddProp = (changedProps) =>({
type:'MULTIPLE_ADD_PROP', changedProps
});
And then react on action in reducer:
const geo=(state,action)=>{
...
switch (action.type){
case 'MULTIPLE_ADD_PROP':
// apply new props
...
}
}
Another way When rerendering is critical :
then you can consider to limit components, which could be rerendered on state change.
For example you can use shouldComponentUpdate to check whether component
should be rendered or not.
Also you could use reselect, in order to not rerender connected components
after calculating derived data...
Non standard way:
redux-batched-action
It works something like transaction.
In this example, the subscribers would be notified once:
import { batchActions } from 'redux-batched-actions';
const multiGeoChanges=(...arrayOfActions)=> dispatch => {
dispatch( batchActions(arrayOfActions) );
}
In react-redux 7.0.1+ batching is now built-in. Release notes of 7.0.1:
https://github.com/reduxjs/react-redux/releases/tag/v7.0.1
Batched Updates
React has an unstable_batchedUpdates API that it uses to group
together multiple updates from the same event loop tick. The React
team encouraged us to use this, and we've updated our internal Redux
subscription handling to leverage this API. This should also help
improve performance, by cutting down on the number of distinct renders
caused by a Redux store update.
function myThunk() {
return (dispatch, getState) => {
// should only result in one combined re-render, not two
batch(() => {
dispatch(increment());
dispatch(increment());
})
}
}
By design when the state, which is held by the store, changes the view should render.
You can avoid this by updating the state once.
If you are using promises you can use Promise.all to wait for all the promises to resolve and then dispatch a new action to the store with the calculated result. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Something like this:
Promise.all([p1, p2, p3, p4, p5]).then(changes => {
dispatch(changes)
}, err => {
// deal with error
});
Of course you'll need an action that will deal with many props, something like addManyProps this should update the state once, resulting in one render.
redux-batched-actions
Batching action creator and associated higher order reducer for redux that enables batching subscriber notifications for an array of actions.
Coming to this a bit late, but I think this is a much nicer solution, which enables you to add meta.batch to actions you would like to batch together into a single react update. As a bonus this approach works with asynchronous actions.
import raf from 'raf'
import { batchedSubscribe } from 'redux-batched-subscribe'
let notify = null
let rafId = null
const shouldBatch = action => action?.meta?.batch
export const batchedSubscribeEnhancer = batchedSubscribe(freshNotify => (notify = freshNotify))
export const batchedSubscribeMiddleware = () => next => action => {
const resolved = next(action)
if (notify && rafId === null && !shouldBatch(action)) {
notify()
} else if (!rafId) {
rafId = raf(() => {
rafId = null
notify()
})
}
return resolved
}
Then connect up to your store
mport { applyMiddleware, compose, createStore } from 'redux'
import { batchedSubscribeMiddleware, batchedSubscribeEnhancer } from './batching'
const store = createStore(
reducer,
intialState,
compose(
batchedSubscribeEnhancer,
applyMiddleware(batchedSubscribeMiddleware)
)
)

Categories

Resources