Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

[React]different ways of fetching asynchronous data into component

I'm a bit new to React world and get confused recently when I start writing Ajax stuff inside components.

As far as I can see, here are 2 ways of rendering asynchronous data into a Component as following:

1st way:

class MyComponent extends React.Component
{
    render() {
      this.props.userList.map((userInfo) => (<span>{userInfo.name}</span>));
      // proceed to render userList
    }
}

//userAjaxSelect is a customized selector based on the Reselect lib
//ajax operation happens here!!
const mapStateToProps = (state, props) => {
  return {
      userList:userAjaxSelect(state, props)
  }
}

const mapDispatchToProps = null;// mapDispatchToProps is omitted here

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

2nd way:

export default class MyComponent extends React.Component
{

  componentDidMount() {
    //FetchUserList is a redux ActionCreator and omitted here
    //it dispatches redux action
    //To get ajax response and sync to local UI state.
    this.setState({userList: FetchUserList(this.props.groupId)})
  }  

  componentWillReceiveProps(nextProps) {
      if(nextProps != this.props)
      //FetchUserList is a redux ActionCreator and omitted here
      //it dispatches redux action
      //To get ajax response and sync to local UI state.
      this.setState({userList: FetchUserList(nextProps.groupId)})
    }

    render() {
      this.state.userList.map((userInfo) => (<span>{userInfo.name}</span>);
      // proceed to render userList
    }
}

From my point of view,

1st solution:

The ajax operation is encapsulated in a Selector which is used in Reselect lib and I think it is pretty clean and can benefit the component performance (by cache)

2nd solution:

Is it a bad one ? because many times I've been warned by a few articles that the componentWillReceiveProps is discouraged to set States (Anti-pattern)? and moreover the way it implements is relatively verbose


Which one is best practice or any other better alternatives? when it comes to loading and refreshing Ajax data

like image 415
Deng Zhebin Avatar asked Dec 31 '25 19:12

Deng Zhebin


1 Answers

I honestly never used any of the approaches you showed. As per documentation you might wanna have any AJAX request in componentDidMount and avoid doing AJAX in componentWillReceiveProps.

The reason behind it is that your component should be mounted even without the necessary data, you don't want to block the view (as it would happen if you used componentWillMount for example) if the request takes too long. The pattern that I see frequently is the following:

export default class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isFetching: false, userList: [] };
  }

  componentDidMount() {
    this.setState({ isFetching: true });
    // In this case FetchUserList is just a simple AJAX call
    // not an action
    FetchUserList(this.props.groupId)
      .then((data) => {
        this.setState({ userList: data, isFetching: false });
      });
  }

  render() {
    this.state.userList.map((userInfo) => (<span>{userInfo.name}</span>);
  }
}

This does not take into account error handling but I hope you get the picture (you can use .catch for it). You can even prevent sending the request if your state is already populated, or avoid setting the state if the data is already there and so on...

On the other hand if you are using redux there are many middlewares that help with that like redux-thunk, alternatively I did create a middleware as well called redux-slim-async that reduces boilerplate and is a combination of what showed in the redux docs and some additional features. At the end of the day use what you are comfortable with and what makes more sense for your code base.


A different approach that makes use of redux could be to just fire the async action inside componentDidMount, then your component listens to the portion of the state (with mapStateToProps) that it's interested in and with componentWillReceiveProps you can update the inner state of the component based on new props received from your manager. What you showed was kind of a hybrid that I would not recommend.

export default class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { userList: [] };
  }

  componentDidMount() {
    // In this case FetchUserList is an Action
    FetchUserList(this.props.groupId)
  }

  componentWillReceiveProps(nextProps) {
     if(nextProps != this.props) {
       this.setState({ userList: nextProps.propFromStateManager });
     }
  }

  render() {
    this.state.userList.map((userInfo) => (<span>{userInfo.name}</span>);
  }
}

Keep in mind that componentWillReceiveProps should be used only if you need to update the inner state of the component based on some props change. The scenario where I would see reselect come in handy is when you want to send the fetch request anyway update the state and then reselect will check for you if the state is exactly the same, in that case it might prevent a re-render. Otherwise I wouldn't fire an ajax request from selector. If I were to jump in your code I would never expect to fins an async call in a selector, that's not what they are for.

Let me know if you need clarification and I will update the answer.

like image 154
G4bri3l Avatar answered Jan 02 '26 11:01

G4bri3l



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!