Reputation: 1181
I have a simple redux application that is displaying articles by requesting data from an api. I want to display "loading" indicator while waiting for promise. How can I implement this?
Upvotes: 5
Views: 1677
Reputation: 8376
Two parties are involved: store and component representation.
In store, per reducer, you should add a prop and call it, say, loading
. So the reducer's initial state would look like
// src/reducers/whatevs.js
const initialState = {
// whatever
loading: false
};
Then, pass this value into container's props with connect
function, like
export default connect(({whatevs}) => ({
// props you want to use in this container, and
loading: whatevs.loading
}));
Next, you want to call an action and retrieve a Promise from it, so it's simply
componentWillMount() {
const promise = requestSomething();
this.props.dispatch(() => {
type: LOADING_SOMETHING
});
promise.then((smth) => this.props.dispatch(() => {
type: LOADED_SOMETHING,
...smth
}));
}
So, first, you claim that you requested something, therefore you're loading; then, you claim that the response has been received, so you're not loading anymore. The corresponding reducers would look like
LOADING_SOMETHING: (state) => ({
...state,
loading: true
})
and
LOADED_SOMETHING: (state, {type, ...smth}) => ({
...state,
...smth,
loading: false
})
And in the component, simply rely on loading
prop to make it render either loading indicator or actual data:
render() {
const {loading} = this.props;
if (loading) {
return (
<LoadingIndicator />
);
} else {
return (
<WhateverComponent />
);
}
}
The connect
function from react-redux package wraps a component and allows it to receive props from the application state aka the Store on every Provider component update. It happens reactively, which means top to bottom, every time you dispatch an action. A store is typically a plain-object (or may it be Immutable.js's Map object, or anything) that, conventionally, contains as many properties as you have reducers.
You can check the value of application state using React DevTools. Just open them, highlight the Provider component, and type
$r.props.store.getState()
You wrap every controlled component (may be called container, or view) with connect
function to make it receive every update of the state and re-render if a part of the app state the component depends on changes.
For example, let's pretend the app state is
{
authentication: {
authenticated: null,
username: null
},
photos: {
items: [],
favorites: []
}
}
and you have a component
export default class Photos extends Component {
render() {
const {
photoList
} = this.props;
return (
<div>{this.renderPhotoList(photoList)}</div>
);
}
}
And what you really want is that photoList
prop of this component would refer to photos.items
property of the app state. Then you connect app state's photos.items
property to component's photoList
prop:
class Photos extends Component {
render() {
const {
photoList
} = this.props;
return (
<div>{doSomethingMeaningful(photoList)}</div>
);
}
}
export default connect((state) => ({
photoList: state.photos.items // remember $r.props.store.getState()?
}))(Photos);
or, with some destructuring assignment,
export default connect(({photos}) => ({
photoList: photos.items
}))(Photos);
The connect
function accepts either one or two arguments, in this particular example there's only one argument that binds component's props to app state's properties.
There could be the second parameter, which is a function of dispatch:
import {
fetchPhotos
} from 'actions/photos';
export default connect(({photos}) => ({
photoList: photos.items
}), {
fetchPhotos
})(Photos);
Which is a different topic.
Need some deeper explanation of how connect
works? See the ref.
Upvotes: 4