Reputation: 9700
In my app I have 2 components (Page
and Home
).
Home
extends Page
like so:
export default class Home extends Page
and Page
just extends Component
.
In Page
I have a method to get user data from AsyncStorage
async getUser() {
// get from storage
this.setState({user});
}
The above is called on the constructor
of Page
.
The problem I have is that Home
has a method on componentWillMount
that relies on this.state.user
. Obviously this isn't going to work since the getUser
method is async.
Is there a way I can get the user information and only call specific methods once I have that info?
export default class Page extends Component {
async getUser() {
// get from AsyncStorage
this.setState({user});
}
componentWillMount() {
this.getUser();
}
}
export default class Home extends Page {
async foo(user_id) {
this.setState({something});
}
componentWillMount() {
this.foo(this.state.user);
}
render() {
return <Text>{this.state.something}</Text>;
}
}
Upvotes: 1
Views: 352
Reputation: 52183
Is there a way I can get the user information and only call specific methods once I have that info?
Not in a general sense, but of course there are some solutions. What this comes down to is that you have an async data dependency. You need to write the code in such a way that a call to dependent functions is only made after the data becomes available.
Perhaps the easiest way to do this is to use componentWillUpdate
instead of componentDidMount
and check if you are receiving the required data:
componentWillUpdate(nextProps, nextState) {
if (nextState.user != null && this.state.user !== nextState.user) {
this.prepareForUser(nextState.user);
}
}
(Note that you can't setState
directly in componentWillUpdate
, but since your method is async it won't happen until later. You can use componentDidUpdate
to chain another setState
call.)
Another option (which I like to use) is to use composition instead of inheritance. This makes the life-cycle easier to control through rendering: the parent can only render
a child when all the child's dependent data is loaded, then the child does not need to worry about any timing issues related to the initial loading of data and can do whatever it wants from its componentDidMount
:
class UserContainer extends Component {
state = {};
componentDidMount() {
this.getUser();
}
async getUser() {
// get user from async store
this.setState({user});
}
render() {
return (
<div>
{ this.state.user ? <UserView user={this.state.user} /> : <Spinner /> }
</div>
);
}
}
class UserView extends Component {
componentDidMount() {
this.props.user != null
}
}
This is pretty much the "container component" pattern.
Upvotes: 2