Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrap a data-fetching component in a HOC to show spinner while loading

I have a bunch of different react components that fetch data over api, they each have corresponding loadData action and isLoading reducer. I'm trying to write a HOC that will show loading spinner in place of component until data is loaded and component can be displayed.

Right now isLoading is true -> spinner rendered -> isLoading becomes false -> child component rendered -> child's componentDidMount() executed -> this again calls loadData action that sets isLoading to false and so on and so on. How to prevent that and call loadData only once? Or maybe there's more elegant solution?

class DataList extends Component {
  componentDidMount() {
    this.props.loadData(); //action that pulls data via api
  }

  render() {
    return <p>{this.props.data}</p>;
  }
}

const withSpinner = (comp) => (props) => {
  if (props.isLoading) {
    return <p>Loading...</p>
  } else {
    return <comp {...props} />
  }
}

export default connect()(withSpinner(DataList));

// action that fetches data and updates state
const loadData = () => {
  setIsLoading(true);
  data = await fetchData();
  setIsLoading(false);
}
like image 975
mgs Avatar asked Oct 18 '25 16:10

mgs


2 Answers

Well, one of the simplest ways you could fix this is by adding a dataLoaded value to wherever you're maintaining your state and set it to true when you've loaded your data. Then, just add a check before calling loadData and that will prevent additional load requests. It would look something like this:

class DataList extends Component {
  componentDidMount() {
    if (!this.props.dataLoaded) { // call it whatever you want
      this.props.loadData(); //action that pulls data via api
    }
  }

  render() {
    return <p>{this.props.data}</p>;
  }
}

const withSpinner = (comp) => (props) => {
  if (props.isLoading) {
    return <p>Loading...</p>
  } else {
    return <comp {...props} />
  }
}

export default connect()(withSpinner(DataList));

// action that fetches data and updates state
const loadData = () => {
  setIsLoading(true);
  data = await fetchData();
  setIsLoading(false);
  setIsLoaded(true);
}

Alternatively, you could also put the dataLoaded inside of the loadData function if you have access to your state inside of that

like image 112
taylorc93 Avatar answered Oct 21 '25 04:10

taylorc93


You have many options here. Very simple solution would be to load data only if it is not present:

componentDidMount() {
  if (!this.props.data) {
     this.props.loadData();
  }
}

You can also introduce new flag into state which will be indicating whether data is loaded or not. Other solution would be to load data directly in HOC.

like image 30
madox2 Avatar answered Oct 21 '25 06:10

madox2