Add to state from Redux middleware - javascript

I've created a redux middleware used for calling my API.
For better debugging I've also implemented status for each of the objects in my state.
Now I have to do this in every case of all reducers:
case SET_USER:
return {
...state,
workspaces: action.response.data.workspaces,
status: action.status, <--
}
Which is pretty repetetive. Is there any way I can change the state (at the correct place) each time automatically?
I do have access to the action type, maybe I could use that somehow?

Related

Redux: Is it common to do deep-compare at reducer and return original state if no change?

Action: fetch an array of data from the server.
Reducer: save an array of data to store.
My reducer saves the array of data in the expected immutable fashion:
return {
...state,
arrayOfData: fetchedArrayOfData,
};
The fetch action happens periodically, but the content of the array remains the same most of the time. However, since a new reference is saved each time, the selector for this data will be considered "changed", and doesn't memoize well if used as an input for creatorSelector.
The only solution I can think of is to perform a deep-compare at the reducer, and if the contents are the same, simply return the original state:
return state;
If this common practice/pattern?
Note: I tried to look around, and most projects are doing the same thing that I was doing (i.e. return new state object), which will do not memoize well and causes selector transformations to run.
There are some solutions like using immutableJS as mentioned in the comments but you can return your state conditionally by comparing your fetchedArrayOfData with the last one (which is stored in your state).
Assume there is a comparison function that gives two arrays and compares them.
In the reducer:
const previousFetchedData = state.fetchedArrayOfData;
const newFetchedData = action.payload.fetchedArrayOfData;
const resultOfComparision = isSameArray(previousFetchedData, newFetchedData) // true or false
if (resultOfComparision) { // case of same arrays
return state
} else { // case of different arrays
...state,
arrayOfData: fetchedArrayOfData,
};
Note 1: you can create your own comparison function but there are many nice ones in this post of StackOverflow which you can use them.
Note 2: using immutableJs and conditional returning data from reducer is common(in such scenario) and don't worry about using them.
Note 3: You can also compare your data at the component level by using the traditional way with shouldComponentUpdate.
Node 4: using middlewares like redux-saga will be useful, you can also implement the isSameArray function in the saga and then dispatch the proper action. read more on saga documentation.
Note 5: The best solution (in my thought) is to handle this case on the backend services with 304 status which means Not modified. then you can easily determine the right action according to the response status. more info on MDN documentation.

Callbacks using redux-thunk / redux-observable with redux

I am learning how redux works but its a lot of code to do simple things. For example, I want to load some data from the server before displaying. For editing reasons, I can't simply just use incoming props but I have to copy props data into the local state.
As far as I've learned, I have to send a Fetch_request action. If successful, a fetch_success action will update the store with new item. Then updated item will cause my component's render function to update.
In component
componentWillMount() {
this.props.FETCH_REQUEST(this.props.match.params.id);
}
...
In actions
export function FETCH_REQUEST(id) {
api.get(...)
.then(d => FETCH_SUCCESS(d))
.catch(e => FETCH_FAILURE(e));
}
...
In reducer
export function FETCH_REDUCER(state = {}, action ={}) {
switch (action.type) {
case 'FETCH_SUCCESS':
return { ...state, [action.payload.id]: ...action.payload }
...
}
Back in component
this.props.FETCH_REDUCER
// extra code for state, getting desired item from...
Instead, can I call a react-thunk function and pass some callback functions? The react-thunk can update the store and callbacks can change the component's local state.
In component
componentWillMount() {
this.props.FETCH_REQUEST(this.props.match.params.id, this.cbSuccess, this.cbFailure);
}
cbSuccess(data) {
// do something
}
cbFailure(error) {
// do something
}
...
In action
export function FETCH_REQUEST(id, cbSuccess, cbFailure) {
api.get(...)
.then(d => {
cbSuccess(d);
FETCH_SUCCESS(d);
}).catch(e => {
cbFailure(d);
FETCH_FAILURE(e);
});
}
...
Is this improper? Can I do the same thing with redux-observable?
UPDATE 1
I moved nearly everything to the redux store, even for edits (ie replaced this.setState with this.props.setState). It eases state management. However, every time any input's onChange fires, a new state is popping up. Can someone confirm whether this is okay? I'm worried about the app's memory management due to redux saving a ref to each state.
First of all, you should call your API in componentDidMount instead of componentWillMount. More on this at : what is right way to do API call in react js?
When you use a redux store, your components subscribe to state changes using the mapStateToProps function and they change state using the actions added a props through the mapDispatchToProps function (assuming you are using these functions in your connect call).
So you already are subscribing to state changes using your props. Using a callback would be similar to having the callback tell you of a change which your component already knows about because of a change in its props. And the change in props would trigger a re-render of the component to show the new state.
UPDATE:
The case you refer to, of an input field firing an onChange event at the change of every character, can cause a lot of updates to the store. As mentioned in my comments, you can use an api like _.debounce to throttle the updates to the store to reduce the number of state changes in such cases. More on handling this at Perform debounce in React.js.
The issue of memory management is a real issue in real world applications when using Redux. The way to reduce the effect of repeated updates to the state is to
Normalize the shape of state : http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
Create memoized selectors using Reselect (https://github.com/reactjs/reselect)
Follow the advice provided in the articles regarding performance in Redux github pages (https://github.com/reactjs/redux/blob/master/docs/faq/Performance.md)
Also remember that although the whole state should be copied to prevent mutating, only the slice of state that changes needs to be updated. For example, if your state holds 10 objects and only one of them changes, you need to update the reference of the new object in the state, but the remaining 9 unchanged objects still point to the old references and the total number of objects in your memory is 11 and not 20 (excluding the encompassing state object.)

Modifying state & the correct way to update my reducer? (React / Redux)

I believe that I have been modifying my state for quite some time now. I wanted to do the following and was wondering why it did not work:
case "SAVE_DATA_TO_PERSON" :
let newState = {...state, data: {start: action.payload.start, end: action.payload.end}};
return newState;
I am here creating a new object, take the old state, and add my new data.
While it does seem to make a difference, it does not keep the data for long. After firing other actions this is just gone. I wonder why?
This is how I used to do it, and it seems to work:
case "SAVE_DATA_TO_PERSON" :
let newState = state;
newState.audio = {start: action.payload.start, end: action.payload.end};
return newState;
But here, it seems, I am modifying state.
I would just like to know whether my first solution is the correct one, and my second solution here is indeed modifying state.
In the second options you are modifying the state directly.
The reducer should changing state in a React-Redux application without mutate existing state.
Immutability is a requirement of Redux because Redux represents your
application state as "frozen object snapshots". With these discrete
snapshots, you can save your state, or reverse state, and generally
have more "accounting" for all state changes. Ref.
You cloud consider using and immutable utility/library as dot-prop-immutable or Immutable.js
Related article from redux docs:
http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html

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.

How to show a loading indicator in React Redux app while fetching the data? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I'm new to React/Redux. I use a fetch api middleware in Redux app to process the APIs. It's (redux-api-middleware). I think it's the good way to process async api actions. But I find some cases which can't be resolve by myself.
As the homepage (Lifecycle) say, a fetch API lifecycle begins with dispatching a CALL_API action ends with dispatching a FSA action.
So my first case is showing/hiding a preloader when fetching APIs. The middleware will dispatch a FSA action at the beginning and dispatch a FSA action at the end. Both the actions are received by reducers which should be only doing some normal data processing. No UI operations, no more operations. Maybe I should save the processing status in state then render them when store updating.
But how to do this? A react component flow over the whole page? what happen with store updating from other actions? I mean they are more like events than state!
Even a worse case, what should I do when I have to use the native confirm dialog or alert dialog in redux/react apps? Where should they be put, actions or reducers?
Best wishes! Wish for replying.
I mean they are more like events than state!
I would not say so. I think loading indicators are a great case of UI that is easily described as a function of state: in this case, of a boolean variable. While this answer is correct, I would like to provide some code to go along with it.
In the async example in Redux repo, reducer updates a field called isFetching:
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
The component uses connect() from React Redux to subscribe to the store’s state and returns isFetching as part of the mapStateToProps() return value so it is available in the connected component’s props:
function mapStateToProps(state) {
const { selectedReddit, postsByReddit } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
return {
selectedReddit,
posts,
isFetching,
lastUpdated
}
}
Finally, the component uses isFetching prop in the render() function to render a “Loading...” label (which could conceivably be a spinner instead):
{isEmpty
? (isFetching ? <h2>Loading...</h2> : <h2>Empty.</h2>)
: <div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Posts posts={posts} />
</div>
}
Even a worse case, what should I do when I have to use the native confirm dialog or alert dialog in redux/react apps? Where should they be put, actions or reducers?
Any side effects (and showing a dialog is most certainly a side effect) do not belong in reducers. Think of reducers as passive “builders of state”. They don’t really “do” things.
If you wish to show an alert, either do this from a component before dispatching an action, or do this from an action creator. By the time an action is dispatched, it is too late to perform side effects in response to it.
For every rule, there is an exception. Sometimes your side effect logic is so complicated you actually want to couple them either to specific action types or to specific reducers. In this case check out advanced projects like Redux Saga and Redux Loop. Only do this when you are comfortable with vanilla Redux and have a real problem of scattered side effects you’d like to make more manageable.
Great answer Dan Abramov!
Just want to add that I was doing more or less exactly that in one of my apps (keeping isFetching as a boolean) and ended up having to make it an integer (which ends up reading as the number of outstanding requests) to support multiple simultaneous requests.
with boolean:
request 1 starts -> spinner on -> request 2 starts -> request 1 ends -> spinner off -> request 2 ends
with integer:
request 1 starts -> spinner on -> request 2 starts -> request 1 ends -> request 2 ends -> spinner off
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching + 1,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching - 1,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
I'd like to add something. The real world example uses a field isFetching in the store to represent when a collection of items is being fetched. Any collection is generalized to a pagination reducer that can be connected to your components to track the state and show if a collection is loading.
It happened to me that I wanted to fetch details for an specific entity that doesn't fit in the pagination pattern. I wanted to have a state representing if the details are being fetched from the server but also I didn't want to have a reducer just for that.
To solve this I added another generic reducer called fetching. It works in a similar fashion to the pagination reducer and it's responsibility is just to watch a set of actions and generate new state with pairs [entity, isFetching]. That allows to connect the reducer to any component and to know if the app is currently fetching data not just for a collection but for an specific entity.
I didn't happen upon this question until now, but since no answer is accepted I'll throw in my hat. I wrote a tool for this very job: react-loader-factory. It's got slightly more going on than Abramov's solution, but is more modular and convenient, since I didn't want to have to think after I wrote it.
There are four big pieces:
Factory pattern: This allows you to quickly call the same function to set up which states mean "Loading" for your component, and which actions to dispatch. (This assumes that the component is responsible for starting the actions it waits on.) const loaderWrapper = loaderFactory(actionsList, monitoredStates);
Wrapper: The component the Factory produces is a "higher order component" (like what connect() returns in Redux), so that you can just bolt it onto your existing stuff. const LoadingChild = loaderWrapper(ChildComponent);
Action/Reducer interaction: The wrapper checks to see if a reducer it's plugged into contains keywords that tell it not to pass through to the component that needs data. The actions dispatched by the wrapper are expected to produce the associated keywords (the way redux-api-middleware dispatches ACTION_SUCCESS and ACTION_REQUEST, for example). (You could dispatch actions elsewhere and just monitor from the wrapper if you wanted, of course.)
Throbber: The component you want to appear while the data your component depends on isn't ready. I added a little div in there so you can test it out without having to rig it up.
The module itself is independent of redux-api-middleware, but that's what I use it with, so here's some sample code from the README:
A component with a Loader wrapping it:
import React from 'react';
import { myAsyncAction } from '../actions';
import loaderFactory from 'react-loader-factory';
import ChildComponent from './ChildComponent';
const actionsList = [myAsyncAction()];
const monitoredStates = ['ASYNC_REQUEST'];
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
const LoadingChild = loaderWrapper(ChildComponent);
const containingComponent = props => {
// Do whatever you need to do with your usual containing component
const childProps = { someProps: 'props' };
return <LoadingChild { ...childProps } />;
}
A reducer for the Loader to monitor (although you can wire it differently if you want):
export function activeRequests(state = [], action) {
const newState = state.slice();
// regex that tests for an API action string ending with _REQUEST
const reqReg = new RegExp(/^[A-Z]+\_REQUEST$/g);
// regex that tests for a API action string ending with _SUCCESS
const sucReg = new RegExp(/^[A-Z]+\_SUCCESS$/g);
// if a _REQUEST comes in, add it to the activeRequests list
if (reqReg.test(action.type)) {
newState.push(action.type);
}
// if a _SUCCESS comes in, delete its corresponding _REQUEST
if (sucReg.test(action.type)) {
const reqType = action.type.split('_')[0].concat('_REQUEST');
const deleteInd = state.indexOf(reqType);
if (deleteInd !== -1) {
newState.splice(deleteInd, 1);
}
}
return newState;
}
I expect in the near future I'll add things like timeout and error to the module, but the pattern's not going to be very different.
The short answer to your question is:
Tie rendering to rendering code--use a wrapper around the component you need to render with the data like the one I showed above.
Add a reducer that makes the status of requests around the app you might care about easily digestible, so you don't have to think too hard about what is happening.
Events and state aren't really different.
The rest of your intuitions seem correct to me.
Am I the only one thinking that loading indicators don't belong in a Redux store? I mean, I don't think it's part of an application's state per se..
Now, I work with Angular2, and what I do is that I have a "Loading" service which exposes different loading indicators via RxJS BehaviourSubjects.. I guess the mechanism is the same, I just don't store the information in Redux.
Users of the LoadingService just subscribe to those events they want to listen to..
My Redux action creators call the LoadingService whenever things need to change. UX components subscribe to the exposed observables...
You can add change listeners to your stores, using either connect() from React Redux or the low-level store.subscribe() method. You should have the loading indicator in your store, which the store change handler can then check and update the component state. The component then renders the preloader if needed, based on the state.
alert and confirm shouldn't be a problem. They are blocking and alert doesn't even take any input from the user. With confirm, you can set state based on what the user has clicked if the user choice should affect component rendering. If not, you can store the choice as component member variable for later use.
We have three types of notifications in our app, all of which are designed as aspects:
Loading indicator (modal or non-modal based on prop)
Error Popup (modal)
Notification snackbar (non-modal, self closing)
All three of these are at the top level of our app (Main), and wired through Redux as shown in the below code snippet. These props control display of their corresponding aspects.
I designed a proxy that handles all our API calls, thus all isFetching and (api) errors are mediated with actionCreators I import in the proxy. (As an aside, I also use webpack to inject a mock of the backing service for dev so we can work without server dependencies.)
Any other place in the app that needs to provide any type of notification simply imports the appropriate action. Snackbar & Error have params for messages to be displayed.
#connect(
// map state to props
state => ({
isFetching :state.main.get('isFetching'), // ProgressIndicator
notification :state.main.get('notification'), // Snackbar
error :state.main.get('error') // ErrorPopup
}),
// mapDispatchToProps
(dispatch) => { return {
actions: bindActionCreators(actionCreators, dispatch)
}}
)
export default class Main extends React.Component{
I'm saving the urls such as::
isFetching: {
/api/posts/1: true,
api/posts/3: false,
api/search?q=322: true,
}
And then I have a memorised selector (via reselect).
const getIsFetching = createSelector(
state => state.isFetching,
items => items => Object.keys(items).filter(item => items[item] === true).length > 0 ? true : false
);
To make the url unique in case of POST, I pass some variable as query.
And where I want to show an indicator, I simply use the getFetchCount variable

Categories

Resources