score:2
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
score:3
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.
score:3
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{
score:5
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
andACTION_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.
score:5
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...
score:13
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.
score:22
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
score:157
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.
Source: stackoverflow.com
Related Query
- How to show a loading indicator in React Redux app while fetching the data?
- Show a loading page when data is fetching - React Redux
- How to delay (e.g. 1s) the showing of a loading indicator in React Redux Saga App?
- How can I update react app that gets it's data from the node mongo backend using redux
- Best way to show a loading spinner/gif while my React component is fetching via AJAX?
- React-query: Show loading spinner while fetching data
- How can I cache data that I already requested and access it from the store using React and Redux Toolkit
- how to hide and show loading spinner - Activity Indicator react native, managing props and state
- How to fetch data in getInitialProps with redux-saga.Then get the response from redux store while in the getInitialProps method?
- How can I use react useContext , to show data from context, in the same component
- How to inject data from the server in index.html in an ASP.NET Core 3 React app
- How to handle multiple api request,to show loading indicator from one variable in redux store
- show loading when fetching data react
- How to fix the state update in react while getting data from firestore database
- How can I render the react app with redux using the ReactJS.NET?
- How to use Promise.all properly fetching data for React app
- How to show fullpage loader/spinner before React App finish loading
- How to refresh a react redux application when data changes on DB outside of the application
- How to handle loading redux state of single items to show a loading indicator
- React Query return undefined data while in network tab the data exists but it does not show on the page
- How to unit test the output of a React component method in a React Router / Redux app with Enzyme
- How to get the data from Redux store using class components in React
- React - How to show an element while hiding the others in React-Hooks
- How to transform the inner state based React app to Redux based?
- How to disable click event while fetching the data in reactjs witout jquery?
- How to show the specified table row data based on the filter option in react js
- How to update React State in Functional Component while fetching data using API
- How to render html tag on React while fetching data from json
- How to show alert while closing the react application in browser tab
- What are the sequence of events when loading xhr data into a react redux routed page?
More Query from same tag
- React - Can't find serviceWorker.js file in app src folder
- Emotion css styled gives me error using same code as example
- Getting state data into a table react
- Not able to redirect after logging in
- app.auth is not a function error in firebase react
- Find object in state and update property
- React form loses focus on update
- Spring method with React front
- How to properly import components in React JS: expected a string but got: object ERROR
- Error: call: argument fn is undefined or null (Redux Saga)
- Convert this function to a stateless react component
- Footer hides content of the div above it
- Should we use PropTypes for Redux state?
- Formik, Material UI Autocomplete and Firestore - where query to find the array parameter
- Redirect if login is correct
- Uncaught TypeError: Super expression must either be null or a function - React
- How to map an array of objects in React
- React in-memory Portal
- Positioning vector images in CSS
- Understanding useState from a jQuery perspective
- React Table + Searching ARRAY
- Target nested material component in JSS style definition
- How to handle Error: socket hang up while waiting Node and React
- React.js state items array not updating properly even not showing any error
- How to render conditional form in reactJS?
- Call a method from another Component after the Async function reactjs
- Do I need a global state in my Reactjs app?
- How can i get access to the current user inside of react from firebase?
- Reactjs - js inside html elements
- How to combine document id and values in firestore