Render component not update after setstate

Viewed 1322

I have a component like this:

... imports

class ChooseGenres extends Component {

    constructor(props) {
        super(props);

        this.setState({
            event: { ... , genres: [] },
            genres: [{...}, {...}, {...}]
        });
    }

    eventHasGenre(item, genre_id) {
        return item.genres.indexOf(genre_id) > -1 ? true : false;
    }

    onGenreChange(genre_id) {
        let event = this.state.event;

        if (this.eventHasGenre(event, genre_id)) {
            event.genres.splice(indexOf, 1)
        } else {
            event.genres.push(genre_id);
        }

        this.setState({
            event: event
        });
    }

    render() {
        return (
            <Container>
                <Content>
                    <List dataArray={this.state.genres}
                        renderRow={(item) =>
                            <ListItem onPress={this.onGenreChange.bind(this, item.id)}>
                                <Body>
                                    <Text>{item.name}</Text>
                                </Body>
                                {this.eventHasGenre(this.state.event, item.id) &&
                                    <Right>
                                        <Icon active name="arrow-forward" />
                                    </Right>
                                }
                            </ListItem>
                        }>
                    </List>
                </Content>
            </Container>
}

ChooseGenres.propTypes = {
    isLoggedIn: PropTypes.bool.isRequired,
}

const mapStateToProps = (state) => {
    return {
        isLoggedIn: state.isLoggedIn
    };
};

const mapDispatchToProps = (dispatch) => ({
    startup: () => dispatch(StartupActions.startup())
});

export default connect(mapStateToProps, mapDispatchToProps)(ChooseGenres);

When I click on ListItem I add the genre to the event.genres array, and I want the Icon to be shown.
But even if the event object in the state is updated correctly (I log it in the console), the Icon is still not shown.

The strange behaviour I noticed is if I back in the navigation and I came back to this Component, the Icons are shown correctly.

2 Answers

First of all you don't need to do setState in the constructor since it is useless as the component has not been rendered

Second, you're trying to mutate the state in onGenreChange function. You dont need extra redundant genres array inside events object, just another tag would work

state = {
  genres: [{id: 1, name: 1}, {id: 2, name: 2}, {id: 3, name: 3}]
}



onGenreChange = (id) => {
    const genres = this.state.genres.map(genre => {
      if (genre.id === id) {
        return {...genre, active: !genre.active}
      }
      return genre
    })
    this.setState({
      genres
    });
  }

<List dataArray={this.state.genres}
      renderRow={(item) =>
        <ListItem onPress={() => this.onGenreChange(item.id)}>
          <Body>
            <Text>{item.name}</Text>
           </Body>
           {item.active &&
             <Right>
               <Icon active name="arrow-forward" size={20} color={'red'}/>
              </Right>
           }
         </ListItem>
        }>
</List>

You are rendering:

this.state.genres

but setting state of:

this.state.event.genres

Try updating your render function to this:

render() {
    return (
        <Container>
            <Content>
                <List dataArray={this.state.event.genres}
                    renderRow={(item) =>
                        <ListItem onPress={this.onGenreChange.bind(this, item.id)}>
                            <Body>
                                <Text>{item.name}</Text>
                            </Body>
                            {this.eventHasGenre(this.state.event, item.id) &&
                                <Right>
                                    <Icon active name="arrow-forward" />
                                </Right>
                            }
                        </ListItem>
                    }>
                </List>
            </Content>
        </Container>
}
Related