reasonml-old / reason-react

632 stars 41 forks source link

Async state updates #69

Closed jayphelps closed 7 years ago

jayphelps commented 7 years ago

Reference of initial discussion: https://twitter.com/_jayphelps/status/842557134994788352


Lifecycles functions currently return option of state, so Some or None, which means they are expected to synchronously return state. In practice, a large number of components will want to make some form of asynchronous side effect (e.g. http request) and then update the state later.

One could use setState for this, however the documentation currently describes setState almost as if it were an anti-pattern, which presents confusion since async state is not rare, it's essential.

image

@chenglou suggested reason-react could adjust the return type to allow return of a Promise No | Sync 'state | Async (Promise 'state) however I feel this presents other problems like how does someone cancel pending async work when the component unmounts? One could also make an argument for Observable instead of Promise. There's even completely different paradigms for managing side effects reason-react could consider like how redux middleware works (redux-saga, redux-observable, etc) or things like cycle.js side effect drivers. There's a lot of potential options.

Mostly recording here so the Twitter convo isn't lost and people can start a discussion.

Here's a pretty standard JS-version of imperative async side effect + cancellation for reference:

class User extends Component {
  state = {
    user: null
  };

  componentDidMount() {
    const { id } = this.props;

    this.subscription = Observable.ajax.getJSON(`/users/${id}`)
      .subscribe(user => {
        this.setState({ user });
      });
  }

  componentWillUnmount() {
    // cancellation (or noop if already done)
    this.subscription.unsubscribe();
  }

  render() {
    const { user } = this.state;

    if (!user) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <h1>Name: {user.name}</h1>
        <p>Age: {user.age}</p>
      </div>
    );
  }
}
jordwalke commented 7 years ago

I feel like this is more about providing facility for side effects in these lifecycle methods - async network requests are just a special case of those, no?

jayphelps commented 7 years ago

@jordwalke 👍 I think we're talking about the same thing.

will want to make some form of asynchronous side effect (e.g. http request) and then update the state later.

chenglou commented 7 years ago

This is now correctly fixed in https://github.com/reasonml/reason-react (sorry for the repo change)!