📜 ⬆️ ⬇️

Data loading from REST API

I want to share another small bike - first of all, to get invaluable advice. Additional examples can be found in the source code of the fan project on GitHub .


Almost all pages in the project are wrapped by the Page component:


const MyPage = () => ( <Page> Hello World </Page> ) 

To load external data, the Page component has three passed properties (props):



Redux example:


 // components/Post/PostViewPage.js const PostViewPage = ({ post, ...props }) => ( <Page {...props}> <Post {...post} /> </Page> ) const mapStateToProps = (state) => ({ post: state.postView, isNotFound: isEmpty(state.postView), }) const mapDispatchToProps = (dispatch, ownProps) => ({ onMounted: () => { const id = parseInt(ownProps.match.params.id, 10) dispatch(actions.read(id)) } }) export default connect(mapStateToProps, mapDispatchToProps)(PostViewPage) 

Note that the isLoading flag is not passed to props explicitly, it is bound via mapStateToProps in the Page component (as will be demonstrated later in the text).


If you have questions about expressions:


 //   - { post, ...props } //  {...props} 

... then you can refer to the MDN directory:



Side effect actions.read (id) provides redux-thunk:


 // ducks/postView.js const read = id => (dispatch) => { //   state.app.isLoading dispatch(appActions.setLoading(true)) //   state.postView dispatch(reset()) //     let isTimeout = false //     let isFetch = false setTimeout(() => { isTimeout = true if (isFetch) { dispatch(appActions.setLoading(false)) } }, 500) //  state.app.isLoading   500  axios(`/post/${id}`) .then(response => { const post = response.data //    state.posts dispatch(postsActions.setPost(post)) //    state.postView dispatch(set(post)) }) .catch(error => { dispatch(appActions.setMainError(error.toString())) }) .then(response => { isFetch = true if (isTimeout) { dispatch(appActions.setLoading(false)) } }) } 

When data loads too quickly, the loading indicator blinks as an unpleasant visual effect. To avoid this, added a 500 ms timer and logic on the isTimeout and isFetch flags.


Component Page, if we discard other decorations, provides the process of loading external data:


 // components/Page/Page.js class Page extends React.Component { _isMounted = false componentDidMount() { this._isMounted = true const { onMounted } = this.props if (onMounted !== void 0) { onMounted() } } render() { const { isNotFound, isLoading, children } = this.props if (this._isMounted && !isLoading && isNotFound) { return <NotFound /> } return ( <div> <PageHeader /> <div> {this._isMounted && !isLoading ? children : <div>...</div> } </div> <PageFooter /> </div> ) } } const mapStateToProps = (state, props) => ({ isLoading: state.app.isLoading }) export default connect(mapStateToProps)(Page) 

How it works? The first pass, the render, will be executed with the _isMounted flag disabled — a display of the load indicator. Next, the lifecycle componentDidMount method is executed, where the _isMounted flag turns on and the onMounted callback function starts; inside onMounted, we cause a side effect (for example, actions.read (id)), where we turn on the state.app.isLoading flag, which will cause a new render - still displaying the load indicator. After an asynchronous call of axios (or fetch) inside our side effect, we turn off the state.app.isLoading flag, which will cause a new render - now, instead of displaying the load indicator, the nested component (children) will be rendered; but if it fulfills the inclusion of the isNotFound flag, then instead of a render for the embedded component (children), the render of the <NotFound /> component will be executed.


')

Source: https://habr.com/ru/post/327422/


All Articles