Cjmarkham
Cjmarkham

Reputation: 9700

React Native persisting user data

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

Answers (1)

Aaron Beall
Aaron Beall

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

Related Questions