Strange behavior

Asked

Viewed 22 times

-2

I have a class component where I run a callback (filterTasks) right after a status change (toggleFilter), all normal and trouble-free.

export default class TaskList extends Component {
    state = {
        showDoneTasks: true,
        showAddTask: false,
        visibleTasks: [],
        tasks: [{
            id: Math.random(),
            desc: 'Comprar Livro de React Native',
            estimateAt: new Date(),
            doneAt: new Date()
        }, {
            id: Math.random(),
            desc: 'Ler Livro de React Native',
            estimateAt: new Date(),
            doneAt: null
        }]
    }

    toggleTask = taskId => {
        const tasks = [...this.state.tasks]
        tasks.forEach(task => {
            if (task.id === taskId) {
                task.doneAt = task.doneAt ? null : new Date()
            }
        })
        this.setState({ tasks }, this.filterTasks)
    }

    componentDidMount = () => {
        this.filterTasks()
    }

    toggleFilter = () => {
        this.setState({ showDoneTasks: !this.state.showDoneTasks }, this.filterTasks)
    }

    filterTasks = () => {
        let visibleTasks = null
        if (this.state.showDoneTasks) {
            visibleTasks = [...this.state.tasks]
        } else {
            const pending = task => task.doneAt === null
            visibleTasks = this.state.tasks.filter(pending)
        }
        this.setState({ visibleTasks })
    }

    render() {
        const today = moment().locale('pt-br').format('ddd, D [de] MMMM')

        return (
            <View style={styles.container}>
                <AddTask
                    isVisible={this.state.showAddTask}
                    onCancel={() => this.setState({ showAddTask: false })}
                />
                <ImageBackground source={todayImage} style={styles.background}>
                    <View style={styles.iconBar}>
                        <TouchableOpacity onPress={this.toggleFilter}>
                            <Icon
                                name={this.state.showDoneTasks ? 'eye' : 'eye-slash'}
                                size={20}
                                color={commonStyles.colors.secondary} />
                        </TouchableOpacity>
                    </View>
                    <View style={styles.titleBar}>
                        <Text style={styles.title}>Hoje</Text>
                        <Text style={styles.subtitle}>{today}</Text>
                    </View>
                </ImageBackground>
                <View style={styles.taskList}>
                    <FlatList
                        data={this.state.visibleTasks}
                        keyExtractor={item => item.id}
                        renderItem={({ item }) =>
                            <Task {...item} toggleTask={this.toggleTask} />
                        }
                    />
                </View>
                <TouchableOpacity 
                    style={styles.addButton}
                    activeOpacity={0.7} 
                    onPress={() => this.setState({ showAddTask: true })}
                >
                    <Icon name='plus' size={20} color={commonStyles.colors.secondary} />
                </TouchableOpacity>
            </View>
        )
    }
}

The "strange" is that for test effect, I entered the toggleFilter method and instead of putting the filterTasks method as the second parameter of setState, I put its direct invocation, just after setState, as below:

toggleFilter = () => {
        this.setState({ showDoneTasks: !this.state.showDoneTasks })
        this.filterTasks()
    }

In theory it was supposed to work normally, but in practice IN THE FIRST TIME toggleFilter is called is as if the line "this.setState({ showDoneTasks: !this.state.showDoneTasks })" did not work, as the attribute showDoneTasks remains true. Only the second time the attribute is changed.

Does anyone know why this difference in behavior?

Thank you for your attention

1 answer

1


Status Updates May Be Asynchronous

If you want to ensure that filterTasks run after the setState, need to use the setState callback:

toggleFilter = () => {
        this.setState({ showDoneTasks: !this.state.showDoneTasks }, () =>{
        this.filterTasks()
    })
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.