Say I have a redux module that looks like this:
import fetch from 'isomorphic-fetch';
// CONSTANTS
const LOAD = 'LOAD';
const LOAD_SUCCESS = 'LOAD_SUCCESS';
// REDUCER
const initialState = { loading: false, data: [] };
export default function reducer(state = initialState, action) {
switch (action.type) {
case LOAD: return { ...state, loading: true };
case LOAD_SUCCESS: return { ...state, loading: false, data: action.data };
default: return state;
}
}
// ACTION CREATORS
function requestLocations() {
return { type: LOAD };
}
function receiveLocations(json) {
return { type: LOAD_SUCCESS, data: json };
}
export function fetchLocations() {
return function (dispatch) {
dispatch(requestLocations());
return fetch('http://myurl')
.then(response => response.json())
.then(json => dispatch(receiveLocations(json)));
};
}
I'm struggling with the loading
state on the first render if I make the async call in componentWillMount. Imagine my component looks like this (simplified for brevity):
export default class LocationContainer extends React.Component {
componentWillMount() {
fetchLocations(); // already bound and injected via connect.
}
render() {
const { loading, data } = this.props; // injected via connect from reducer above.
if (loading) {
return <Spinner />;
} else {
return <LocationExplorer locations={ data } />;
}
}
}
The problem I run into is that on the first render of LocationContainer
, loading
is false and data
hasn't been fetched yet. In componentWillMount
, the LOAD
action is fired and a props change of loading
being set to true is queued up to happen on the subsequent render. In the meantime, during my first render, LocationExplorer
is rendered when I really wanted Spinner
instead because loading
is still false. I'm wondering how you deal with this without setting a firstRender = true
state variable hack.
The first thing we have to do is to create a separate reducer that will manage loading stuff. As we already know, we want a separate state for each request so, naturally, we can store these loading states in the array. This array will represent all active requests in our application.
Redux Async Data Flow Just like with a normal action, we first need to handle a user event in the application, such as a click on a button. Then, we call dispatch() , and pass in something, whether it be a plain action object, a function, or some other value that a middleware can look for.
UseSelector and useDispatch in React Redux useSelector and useDispatch are a set of hooks to use as alternatives to the existing connect() higher-order component. The equivalent of map state to props is useSelector. It takes in a function argument that returns the part of the state that you want.
However, the flow of Redux's state management tasks is completely synchronous: dispatching an action immediately generates the chain of calls to middleware and reducers to carry out the state transition.
One option could be extend your loading
condition with your data
initial state:
const initialState = { loading: false, data: [] };
When you are loading
and your data
is empty it means you are in this exact state of waiting for new data to come:
if (loading && data.length === 0) {
return <Spinner />;
} else {
Also, I usually put my asynchronous calls in componentDidMount
instead of componentWillMount
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With