Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React "forgets" state when using debounce function

This is a question for someone who understands React/RN better than I. When I wrap touchable components (i.e. buttons) in a lightweight wrapper with a debounce function (to prevent onPress handler from being called too rapidly), it usually works as intended. HOWEVER, in a very specific set of circumstances, things go awry. If I pass a parent component's state down into the onPress prop of the button, if the state property I'm accessing has been destructured in the render method and then passed as this variable, rather than simply accessed as this.state.foo, the onPress handler reads it as its initial state value, even if it has been updated. This is probably confusing so allow me to show you a quick, incomplete example:

class DebounceButton extends Component {
    handlePress = debounce(this.props.onPress, 500)
    render() {
        return (
            <Button onPress={this.handlePress}
        )
    }
}

class Screen extends Component {
    state = {
        foo: 0
    }

    render() {
        const { foo } = this.state
        return (
            <Button onPress={() => {this.setState({ foo: this.state.foo + 1 })}}/>
            <DebounceButton onPress={() => {console.log(foo)}}/>
            <DebounceButton onPress={() => {console.log(this.state.foo)}}/>
        )
    }
}

If I press Button, foo increments to 1. If I then press the first DebounceButton, the console will log 0. If I press the second, it will log 1 as it should. Note it only happens when state is passed through the debounce function AND assigned to a variable in the render method. It's as if React is "forgetting" what the current state is and default to its initial value. This is no longer a bug for me as I'm not using this debounce paradigm anymore, but I'm curious to understand it to better so I can have a better grasp on the way React works. Any insight is greatly appreciated.

like image 919
kevbot Avatar asked Dec 06 '25 11:12

kevbot


1 Answers

The class property handlePress = debounce(this.props.onPress, 500) will only be evaluated when DebounceButton is first created, so changing the onPress after it has first been rendered does not work.

You could instead create a new function that invokes this.props.onPress inside of it. This way the current value of this.props.onPress will be used every time.

class DebounceButton extends Component {
    handlePress = debounce(() => {
        this.props.onPress();
    }, 500);

    render() {
        return <Button onPress={this.handlePress} />;
    }
}
like image 144
Tholle Avatar answered Dec 08 '25 01:12

Tholle