Promise state with React+Mobx

I'm pretty sure that you end up with a situation when your component need to fetch data from the database and this process envolve

  • Display loading status
  • Display error if the promise is rejected
  • Update data if the promise is resolved

Whats is the effective way manage this states isLoading , error in react? 
Sounds like a simple answer, so ..

Let’s put an example:

Challenge #1

Build a TodoList component that should be able

  1. Fetch todos from server
  2. Show loading state for user whiling fetching
  3. If any error, display error
  4. After fetching, render todos list

It’s a good practice resolve promises soon as possible, so make sense use componentWillMount for that.. But ...

And after make some research …

Can be deprecate

There are a big discussion on issue #7671 and eslint-react-plugin about deprecate componentWillMount in flavor of constructor()

Will not call the render again

As componentWillMount doesn't trigger a re-render you won't have the behaviour you expected which is the component being rendered with the requested data.
— — stackoverflow.com/a/43340442/491181

It’s used on serve-side render

is rarely useful; especially if you care about server side rendering (adding event listeners causes errors and leaks, and lots of other stuff that can go wrong).
— — stackoverflow.com/a/27139507/491181

..

Invoked once, both on the client and server, immediately before the initial rendering occurs.
— - https://discuss.reactjs.org/t/constructor-vs-componentwillmount-vs-componentdidmount/4287/4

You can’t cancel ajax

To not leak memory its a usually a good practice to cancel this request when the component is unmounted,
.. whatever reason the component did not mount. you still have your pending ajax request which can not be cancelled anywhere
— — stackoverflow.com/a/39365138/491181

Thats the reason that React docs recommend componentDidMount instead componentWillMount

If you need to load data from a remote endpoint, this is a good place to instantiate the network request.

Using componentDidMount

Following the react documentation, let’s put fetch request on componentDidMount.

To complete our task we need to notify the component that we are loading and after that, if there are any error, show the error.

componentDidMount() {
const { fetchTodos } = this.props.todoStore;
this.setState({ isFetching: true }); // OPS
const result = fetchTodos();
when(() => result.state !== PENDING,
() => {
this.setState({ isFetching: false });
result.case({
rejected: error => this.setState({ error }),
});
},
)
}

Perfect example..

We make a request in fetchTodos that return an fromPomise so we can use the result to set the loader this.setState({ isFetching: false });and error this.setState({ error }) if necessary.

But, wait.. there are a problem here.. this.setState({ isFetching: true }); // OPS will make the the component render again.

Doing setState in componentDidMount will cause a visible render flash.
--- github.com/airbnb/javascript/issues/684#iss..

Thats the reason that has a ESLint rule that prevent you to use setState on componentDidMount

They are a workaround for that.. simple put your initial state as isFetching :true in constructor

constructor(props) {
super(props);
this.state = {isFetching: true};
}

This will make your TodoList start in loading mode, but this is ok.

The reason that this.setState({ isFetching: false }); inside the componentDidMount works because when is "watching" the property state of result, when the property change the callback will execute. So it's not executed in context of componentDidMount it's executed in context of when.

Chalenge #2

Build a TodoView component that display a single todo

  1. Should be able to complete/uncomplete the todo
  2. Should display status while complete/uncomplete todo

We can follow the same patter..

constructor(props) {
super(props);
this.setCompleted = this.setCompleted.bind(this);
this.state = {isLoading: false};
}

When user click on complete, we can "watch" for the property state and react on change

setCompleted(isCompleted) {
const { todo, todoStore } = this.props;
this.setState({ isLoading: true });
const result = todoStore.complete(todo.id, isCompleted);
when(() => result.state !== PENDING,
() => {
this.setState({ isLoading: false });
result.case({
rejected: error => this.setState({ error }),
fulfilled: (value) => {
value.complete
? $(this.checkInput).checkbox('check')
: $(this.checkInput).checkbox('uncheck')
}
});
});
}

We are not inside of componentDidMount so it's ok to use this.setState({ isLoading: true }); on beginner.

full code

Like this? Please click ❤️ bellow so other people can find it.