0

so here i have 3 TouchableOpacities , for each press the backgroundColor changes its self and it works fine , so i tried adding in my changeColor() function a setState that returns a size (M,L or XL) of each TouchableOpacity pressed here is my code :

    constructor(props) {
        super(props)
            colorId: 0,
            size:""
        };
    }
        changeColor = (id) => {      
        this.setState({ colorId: id });
        if (id == 1) {
            this.setState({ size: 'M' })
        }
        else if (id == 2) {
            this.setState({ size: 'XL' })
        }
        else {
            this.setState({ size: 'L' })
        }
        console.log("Button id:", id ,"size :", this.state.size)
    }
     render() {



        return (
                            
<TouchableOpacity style={this.state.colorId === 2 ? styles.button_XL_Colored : styles.button_XL} onPress={() => this.changeColor(2)} ><Text style={{ color: '#000000', alignSelf: 'center', marginTop: normalize(12), fontSize: normalize(20) }}>XL</Text></TouchableOpacity>
<TouchableOpacity style={this.state.colorId === 3 ? styles.button_L_Colored : styles.button_L}   onPress={() => this.changeColor(3)}><Text style={{ color: '#000000', alignSelf: 'center', marginTop: normalize(12), fontSize: normalize(20) }}>L</Text></TouchableOpacity>
<TouchableOpacity style={this.state.colorId === 1 ? styles.button_M_Colored : styles.button_M} onPress={() => this.changeColor(1)} ><Text style={{ color: '#000000', alignSelf: 'center', marginTop: normalize(12), fontSize: normalize(20) }}>M</Text></TouchableOpacity>
                    


        );
    }
const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5F5F8',
    },

    button_M: {
        backgroundColor: '#FFFDFD',
        borderRadius: 10,
        width: normalize(50),
        height: normalize(50),
        alignSelf: 'center',
        marginLeft: 0,
        marginTop: normalize(-10)
    },
    button_L: {
        backgroundColor: '#FFFDFD',
        borderRadius: 10,
        width: normalize(50),
        height: normalize(50),
        alignSelf: 'center',
        marginLeft: normalize(140),
    },
    button_XL: {
        backgroundColor: '#FFFDFD',
        borderRadius: 10,
        width: normalize(50),
        height: normalize(50),
        alignSelf: 'center',
    },
    button_M_Colored: {
        backgroundColor: '#D05A0B',
        borderRadius: 10,
        width: normalize(50),
        height: normalize(50),
        alignSelf: 'center',
        marginLeft: 0,
        marginTop: normalize(-10)
    }, 

    button_XL_Colored: {
        backgroundColor: '#D05A0B',
        borderRadius: 10,
        width: normalize(50),
        height: normalize(50),
        alignSelf: 'center',
    },
    button_L_Colored: {
        backgroundColor: '#D05A0B',
        borderRadius: 10,
        width: normalize(50),
        height: normalize(50),
        alignSelf: 'center',
        marginLeft: normalize(140),
    },
The problem is i cannot get the size value that i wanted at first press enter image description here

it should return size="M" when i press the TouchableOpacity thats has the M letter

its only working if it press twice :

enter image description here

the same case for other touchableOpacities :

enter image description here

Is there any solution for this ?

melek hedhili
  • 67
  • 2
  • 8
  • setState updates state asynchronously. Use the callback param if you want to log like that https://reactjs.org/docs/react-component.html#setstate – Andy Ray May 06 '21 at 01:36
  • Does this answer your question? [Why calling react setState method doesn't mutate the state immediately?](https://stackoverflow.com/questions/30782948/why-calling-react-setstate-method-doesnt-mutate-the-state-immediately) – Emile Bergeron May 06 '21 at 02:15

2 Answers2

6

You're using console.log right after setState, above results are expected because setState is asynchronous. So console.log is executed with old state instead of new state (new state has not been assigned yet).

You need to use the callback form of setState to get the right state.

this.setState({ size: 'L' }, (state) => console.log("Button id:", id ,"size :",state.size))

You can read more about it here: https://reactjs.org/docs/react-component.html#setstate

// Added (I don't have enough rep to comment)

I saw John Lim's answer that suggests to use await, but setState does not return a Promise, so await won't work here. https://github.com/facebook/react/blob/0e100ed00fb52cfd107db1d1081ef18fe4b9167f/packages/react/src/ReactBaseClasses.js#L57-L66

moonlight8978
  • 156
  • 1
  • 3
  • 1
    Hi @moonlight8978, you are right! I have deleted my answer due to it is not the proper way to solve the problem, but async-await indeed did the trick. you may refer to this https://stackoverflow.com/questions/47019199/why-does-async-await-work-with-react-setstate – John Lim May 06 '21 at 10:12
  • I have never heard about this trick before. Thanks @JohnLim – moonlight8978 May 06 '21 at 10:35
0

console.log() does not show things immediately after useState().

One alternative might be putting console.log inside useEffect().

Another solution might be using setTimeOut() by calling the method after a certain moment.

naimur978
  • 144
  • 8