Reputation: 407
in my app (React, Redux, React-Router, Express) I would like to make Higher Order Component for fetching initial data for each view. It will send requests (one or more per each view) and display Loader Component until all requests succeed and then displays proper component.
const FetchData = (ComposedComponent) => {
class FetchData extends React.Component {
constructor(props){
super(props)
this.state={isLoading: true}
}
componentDidMount(){
this.fetchData()
}
fetchData() {
const that = this
Promise.all(
Object.keys(this.props)
.filter(element => _.isFunction(this.props[element]))
.map(element => this.props[element])
).then(result => that.setState({isLoading:false})
)
}
displayContent(){
let content = this.state.isLoading ? <Loader /> : <ComposedComponent {...this.props} />
return content
}
render() {
return (
<div>
{this.displayContent()}
</div>
)
}
}
return FetchData;
}
export default FetchData
And here is my component to be wrapped and finally display:
import requireData from '../utils/requireData'
class Photos extends Component {
render() {
return (
<h1>Photos</h1>
);
}
}
export default connect(null, {getTodos,getPhotos})(requireData(Photos))
My solution is to build HOC which wraps MainComponent (Container) and gets all needed actions via props. It fires all actions in componentDidMount, then store is updated and at the end MainComponent (using connect) is rendered. It looks quite simple but problem occurs when in actions fetching initial data I have to use params eg. userId from store oraz any other ID from url (react-router). Some actions need additional arguments and some not.
There is another problem... setState fires before Promise.all() succeed. How can I handle with it? How to pass proper arguments to HOC?
Regards
Upvotes: 1
Views: 1712
Reputation: 407
Thank you for your advices in this question and another. I managed to make my HOC for fetching data in Redux working. It still needs improvements but works :) I post my solution - maybe someone will benefit. Here is repo: https://github.com/krzysztof4M/hoc-data-redux
Advices and suggestions are really welcome.
This is my HOC:
import React, { Component } from 'react'
import _ from 'lodash'
import Loader from '../components/Loader'
const FetchData = (ComposedComponent) => {
class FetchData extends Component {
constructor(props){
super(props)
this.state={
isLoading: true
}
}
componentDidMount(){
this.fetchData()
}
fetchData(){
const that = this;
Promise.all(
Object.keys(this.props)
.filter(el => _.isFunction(this.props[el]))
.map(el => this.props[el]())
)
.then(() => {
console.log('I did everything!', new Date().getTime());
that.setState({isLoading:false})
})
}
displayContent(){
const { isLoading } = this.state
let content = isLoading ? <Loader /> : <ComposedComponent {...this.props}/>
return content
}
render() {
return (
<div>
{this.displayContent()}
</div>
);
}
}
return FetchData
}
export default FetchData
And this is my implementation of HOC in components (maybe a little strange):
import React, { Component } from 'react'
import { connect } from 'react-redux'
import requireData from '../utils/requireData'
import { getTodos } from '../actions/todosActions'
import { getPhotos } from '../actions/photosActions'
import { getUsers } from '../actions/usersActions'
class Todos extends Component {
render() {
return (
<div>
<h1>Users: {this.props.users.length}</h1>
<h1>Photos: {this.props.photos.length}</h1>
<h1>Todos: {this.props.todos.length}</h1>
</div>
);
}
}
function mapStateToProps(state){
return {
todos: state.todos,
photos: state.photos,
users: state.users
}
}
export default connect(null,{ getTodos, getPhotos, getUsers })(requireData(connect(mapStateToProps)(Todos)))
Upvotes: 1