I'm pretty sure that you end up with a situation when your component need to fetch
data from the database and this process evolve
- 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
- Fetch todos from server
- Show loading state for user whiling fetching
- If any error, display error
- 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.
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).
and ..
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
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
incomponentDidMount
will cause a visible render flash.
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
- Should be able to complete/uncomplete the todo
- 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.