Ryan T. J.
Ryan T. J.

Reputation: 41

React Native setState not working (functional component) at all, not even just out of sync

I appreciate anyone taking the time to go through my code and helping me with what's going wrong (printLang() is always printing 'en' or 'es' depending on which value it took on when I refreshed the app)! I'm using Material Dropdown for the first time and don't know if that's affecting my state management, so I'm going to just paste the whole code here!

import React, { useCallback, useState } from 'react';

const LANGS = [
    {
        value: 'en',
    }, 
    {
        value: 'es',
    }
];

const INTRO_DATA = [
    {
        key: '1',
        title: 'Language Selection',
        description: 'Choose your preferred language',
    },
    {
        key: '2',
        title: 'Role Selection',
        description: 'Are you a worker or a organization / business owner?',
    }
];

const IntroScreen = ({navigation}) => {
    const [appLanguage, setAppLanguage] = useState("en");
    const { width } = Dimensions.get('screen');
    const scrollX = React.useRef(new Animated.Value(0)).current;

    const printLang = () => {
        if (appLanguage == "en") {
            setAppLanguage("es");
        }
        else {
            setAppLanguage("en");
        }
        console.log(appLanguage);
    }

    const languageSelectionDropdown = (
        <View style={styles.test}>
            <Dropdown 
                width={150} 
                maxHeight={150} 
                label='Language' 
                data={LANGS}
                onChangeText={(value, index, data) => {
                    console.log(value);
                    console.log(index);
                    console.log(data);
                    () => {setAppLanguage(value)};
                }}
                baseColor={Colors.darkGray}
                textColor={Colors.darkGray}
                containerStyle={styles.dropDownStyle}
            />
            <TouchableOpacity style={styles.touchable} onPress={() => printLang()}>
                <Text>
                    Next
                </Text>
            </TouchableOpacity>
        </View>
    );

    const testmodule = (
        <View style={styles.buttonsContainer}>
            <Text>Empty for now</Text>
        </View>
    );
    
    const renderItem = React.useCallback(
        ({ item, index }) => {
            const inputRange = [
                (index - 1) * width,
                index * width,
                (index + 1) * width,
            ];
            const descriptionTranslate = scrollX.interpolate({
                inputRange,
                outputRange: [width * 0.1, 0, -width * 0.1],
            });
            return (
                <View style={[styles.itemContainer, { width: width - 80 }]}>
                    <Text style={styles.title}>{item.title}</Text>
                    <Animated.Text style={{ transform: [{ translateX: descriptionTranslate }] }}>
                        {item.description}
                    </Animated.Text>
                    <View>
                        {item.key == '1' ? languageSelectionDropdown : testmodule}
                    </View>
                </View>
            );
        },
        [scrollX, width]
    );

    const keyExtractor = React.useCallback((item) => item.key, []);

    return (
        <View style={[styles.container]}>
          <FlatList
            data={INTRO_DATA}
            keyExtractor={keyExtractor}
            showsHorizontalScrollIndicator={false}
            onScroll={Animated.event(
              [{ nativeEvent: { contentOffset: { x: scrollX } } }],
              {
                useNativeDriver: false,
              }
            )}
            style={styles.flatList}
            pagingEnabled
            horizontal
            decelerationRate={'normal'}
            scrollEventThrottle={16}
            renderItem={renderItem}
          />
          <View style={styles.text}>
            <View style={styles.dotContainer}>
              <Text>Scaling Dot</Text>
              <ScalingDot
                data={INTRO_DATA}
                scrollX={scrollX}
                containerStyle={{
                  top: 30,
                }}
              />
            </View>
          </View>
        </View>
      );    
};

In any single run of the app, the button in languageSelectionDropdown calls printLang() and always prints the same value even if I switch between the languages using the dropdown. I've tried doing a console log in onChangeText itself and am sure that the value of the dropdown is indeed changing, so it's just the state management that is going awry.

Appreciate any help! :)

Upvotes: 4

Views: 270

Answers (2)

P.B.UDAY
P.B.UDAY

Reputation: 483

The state gets updated asynchronously and so the printLang() function won't see the change until the component rerenders.

Because the setState function is async, you can use useEffect hook to achieve the functionality you are looking for.

useEffect(() => { 
 // call any function you want to
 // this will only execute after appLanguage is updated. 

},[appLanguage])

Upvotes: 1

Stas  Kravchuk
Stas Kravchuk

Reputation: 146

Seems like you forget to add languageSelectionDropdown as a dependency to your renderItem function with useCallback. As you didn't add it so renderItem function never see the changes of the state and that's why always gives you the same value.

Upvotes: 1

Related Questions