Bruno
Bruno

Reputation: 503

React Native setState(…) warning with both componentWillMount and componentDidMount

I'm starting with react-native and in my project I got to a point where everything works but there's this warning:

Warning: setState(...): Can only update a mounted or mounting component.

So, I've looked several QA, tried a few solutions(changing the setState() call from componentWillMount and componentDidMount) but... the warning is always there.

Here is part of the code:

REQUEST_URL = 'http://url/users.php';

(...)

module.exports = React.createClass({
    getInitialState: function() {
        return {
            uid: null,
            bid: null,
            username: null,
        }
    },

    componentDidMount: function() {
        this.fetchData();
    },
    fetchData: function() {
        fetch(REQUEST_URL)
            .then( (response) => response.json() )
            .then( (json) => {
                console.log('setState called');
                this.setState({
                    uid: json.users[0].user_id,
                    bid: json.users[0].building_id,
                    username: json.users[0].username
                });
            })
            .done();
    },
    render: function() {
        if (!this.state.uid) { //user is not defined
            console.log('not rendered');
            return <Text>chargement...</Text>
        }
        // else
        console.log('rendered');
        var userId = this.state.uid;
        var buildingId = this.state.bid;
        var username = this.state.username;

        return (
            <View style={styles.content}>
                <Text style={styles.label}>User Id</Text>
                <Text>{userId}</Text>

                <Text style={styles.label}>Building Id</Text>
                <Text>{buildingId}</Text>

                <Text style={styles.label}>Username</Text>
                <Text>{username}</Text>
            </View>
        )
    },
});

The users.php returns a json content-type.

Any clues?

Thanx.

Upvotes: 2

Views: 1226

Answers (1)

Julius Breckel
Julius Breckel

Reputation: 434

The problem may be that react re-mounts certain components multiple times in one render (think that has something to do with the representation of initial values, could not find the question here), therefore your state would be set to a component that is not mounted.

If you set your state in a decoupled timeout that can be cleared when the component unmounts, you avoid setting state on a unmounted component.

componentDidMount() {
  this.mounted = true;
  // this.fetchTimeout = setTimeout(()=>{
    this.fetchData();
  // });
},
componentWillUnmount() {
  // clearTimeouts(this.fetchTimeout);
  this.mounted = false;
},
fetchData() {
    fetch(REQUEST_URL)
        .then( (response) => response.json() )
        .then( (json) => {
            console.log('setState called');
            if (this.mounted === true){
               this.setState({
                  uid: json.users[0].user_id,
                  bid: json.users[0].building_id,
                  username: json.users[0].username
              });
            }
        })
        .done();
},

I still don't know if we are supposed to use TimerMixins but this way works without those. (TimerMixins take care of clearing any timeout or interval set in the component)

EDIT: update sample to only call setState of the component is still mounted. I do not know if there is a better way, but as far as I know until now you can not cancel a fetch request.

Upvotes: 2

Related Questions