1
votes

I have a big problem. I have screen which receive some props from another screen, I pass those props into component in thath screen via <TicketDiaryHour {...this.props} />. I set those props in component into my state in method componentDidMount().

In that component (TicketDiaryHour) I also have flatlist. My problem is that I change data in flatlist (via setState) and props is also changing...I don't know why this happens.

My componentDidUpdate() returns prevProps == this.props and also prevState == this.state. I don't know why...

Look:

Prev props: {"navigation":{"state":{"params":{"props":{"id":"418103","headingText":"Klavir","roomText":"Mala dvorana","student":"Ives Berlovčnik","apsent":"1/1","startTime":"07:00","endTime":"07:30","finished":"0","listOfStudents":[{"nameLastname":"Ives Berlovčnik","id":"43275","checked":false,"o":true,"n":false}],"minutes":"30","notes":"","lesson":""}}}

This props: {"navigation":{"state":{"params":{"props":{"id":"418103","headingText":"Klavir","roomText":"Mala dvorana","student":"Ives Berlovčnik","apsent":"1/1","startTime":"07:00","endTime":"07:30","finished":"0","listOfStudents":[{"nameLastname":"Ives Berlovčnik","id":"43275","checked":false,"o":true,"n":false}],"minutes":"30","notes":"","lesson":""}}}

Component code: Flatlist;

<FlatList
                                    ref={(list) => this.myList = list}
                                    style={[styles.flatList,{height: this.state.height}]}
                                    data={this.state.data}
                                    scrollEnabled = {this.state.showList ? true : false}

                                    contentContainerStyle={{ padding: 15 }}
                                    renderItem={({ item }) => (

                                          <View style={styles.listItemStyle}>
                                            <View style={{flexDirection: 'row', marginBottom: 7, }}>

                                                {
                                                    item.checked && 
                                                    <TouchableOpacity
                                                        onPress={this.changeCheckedToFalse.bind(this,item)}>
                                                        <View style={styles.checked} /> 
                                                    </TouchableOpacity> || 
                                                    <TouchableOpacity
                                                        onPress={this.changeCheckedToTrue.bind(this,item)}>
                                                        <View style={styles.unchecked} />
                                                    </TouchableOpacity>
                                                }


                                                <Text style={{color: '#000', opacity: 0.6}}>{item.nameLastname}</Text>
                                                {
                                                    item.checked &&
                                                    <View style={{position: 'absolute', right: 0 }}>
                                                        <View style={{flexDirection: 'row'}} >
                                                        {
                                                            item.o &&
                                                            <TouchableOpacity 
                                                                style={[styles.touchable1Ch,styles.iconStyle1]} 
                                                                onPress={this.changeSelectionO.bind(this,item)}>

                                                                <Text style={{color: '#fff', fontSize: 18, alignSelf: 'center' }}>O</Text>
                                                            </TouchableOpacity> ||
                                                            <TouchableOpacity 
                                                                style={[styles.touchable1,styles.iconStyle1]} 
                                                                onPress={this.changeSelectionO.bind(this,item)}>

                                                                <Text style={{color: '#fff', fontSize: 15, alignSelf: 'center' }}>O</Text>
                                                            </TouchableOpacity>
                                                        }
                                                        {
                                                            item.n &&
                                                            <TouchableOpacity 
                                                                style={[styles.touchable2Ch,styles.iconStyle1]} 
                                                                onPress={this.changeSelectionN.bind(this,item)}>
                                                                <Text style={{color: '#fff', fontSize: 18, alignSelf: 'center' }}>N</Text>
                                                            </TouchableOpacity> ||
                                                            <TouchableOpacity 
                                                                style={[styles.touchable2,styles.iconStyle1]}
                                                                onPress={this.changeSelectionN.bind(this,item)}>
                                                                <Text style={{color: '#fff', fontSize: 15, alignSelf: 'center' }}>N</Text>
                                                            </TouchableOpacity>
                                                        }




                                                        </View>
                                                    </View>
                                                }
                                            </View>
                                            {
                                                this.props.navigation.state.params.props.listOfStudents !== undefined && this.props.navigation.state.params.props.listOfStudents.length >= 2 ?
                                                <View style={styles.line} /> : <Text></Text>
                                            }

                                          </View>



                                    )}
                                    keyExtractor={item => item.id}
                                />

How I load data in thath component:

componentDidMount() {



        this.setState({
            data: this.props.navigation.state.params.props.listOfStudents,
            textOpombe: this.props.navigation.state.params.props.notes,
            textVsebinaUre: this.props.navigation.state.params.props.lesson,
            finished: this.props.navigation.state.params.props.finished,
            absent: parseInt(this.props.navigation.state.params.props.apsent.substring(0,1)),
        });
}

How I change data in flatlist;

changeCheckedToFalse = (item) => {
      console.log('Item');
        var arr = this.state.data;

        var indexOfItem = arr.indexOf(item);
        arr[indexOfItem].checked = false;
        var abs = this.state.absent;
        abs -= 1;
        this.setState({
            data: arr,
            //scrollingParent: false,
            //scrollingChild: true,
            absent: abs,
        });

          console.log('State after changing: ' + JSON.stringify(this.state.data));
    }

And final code; When change flatlist item, this method is trigerred

componentDidUpdate(prevProps, prevState) {
          console.log('Prev props: ' + JSON.stringify(prevProps));
          console.log('This props: ' + JSON.stringify(this.props));

          console.log('Prev state: ' + JSON.stringify(prevState));
}
2
AFAIK setState() triggers a render, right? If you don't want it to happen you should write your own version of componentShouldUpdate(nextProps, nextState).rrd
Also nextProps and nextState is the same as this.props and this.state...mkEagles

2 Answers

1
votes

It looks to me like you're actually mutating this.state.data / this.props.navigation.state.params.props.listOfStudents - as they are the same.

I presume you're including this line: var arr = this.state.data; in an effort to copy the value in this.state.data, but I'm afraid it doesn't work like that for reference types (such as arrays). arr actually points to the exact same array as this.state.data and this.state.data points to the exact same array as this.props...listOfStudents

So when you change a value: arr[indexOfItem].checked = false; you are actually mutating the props.

One of the very important rules of React is "don't mutate anything", so how would you go about fixing this?

The easiest way would be to properly copy the array before changing it:

Using the spread operator
var arr = [...this.state.data];

Or the Array.prototype.slice method
var arr = this.state.data.slice()

Edit: While the above is true and would fix your issue if you had an array of primitives, you actually have an array of object references, these have the same exact issue as described above.

Copying the array doesn't copy the objects inside, only references to those objects, so you have the same issue. Try a deep copy of the array:

var arr = this.state.data.map(x => ({...x}));

Or alternatively only copy the object you're changing:

var arr = [...this.state.data];
var indexOfItem = arr.indexOf(item);

arr[indexOfItem] = {...item, checked: false};
0
votes

Why do you say the props have changed? The prevProps and this.props that you specified in your question are the same. When you use the setState method, the component is updated, triggering the componentDidUpdate lifecycle method to run. It doesn't mean that your props have changed.