Search code examples
javascriptreactjsreact-nativeasyncstorage

AsyncStorage mismatch data with hook react native in debug mode


I have 2 separate screens connected with simple react native stack router. On second screen I have choice blocks with setting state to useState and also to AsyncStorage, on every enter the screen, also it reads state of AsyncStorage on every entry as initial state of useState hook. On first screen I have list blocks that navigating to specific screen, in 2 blocks language and currency I have "label" that shows state from AsyncStorage and here is the problem. I have useEffect hook called on useFocused hook, that update this "labels" it works fine with debug mode on, but without debug mode it setting previous picked value, the same effect in apk build.

This is first screen:

const ListLine = (props) => {
    return  <TouchableOpacity style={styles.line} onPress={props.onPress}>
                <View style={styles.line_text}>
                    {props.label ? <Text style={styles.label}>{props.label}</Text> : null}
                    {props.labelLong ? <Text style={styles.label_long}>{props.labelLong}</Text> : null}
                    {props.value ? <Text style={styles.line_title}>{props.value}</Text> : null}
                </View>
                <View style={styles.icon}><IconArR/></View>
            </TouchableOpacity>
}

const SettingsScreen = (props) => {

    const insets = useSafeArea();
    const {navigation} = props;
    const userInfo = useSelector(state => state.userInfoReducer)
    const dispatch = useDispatch()
    const [selectedCountry, setSelectedCountry] = useState(userInfo.country)
    const [selectedLanguage, setSelectedLanguage] = useState(null)
    const [selectedCurrency, setSelectedCurrency] = useState(null)
    const isFocused = useIsFocused()

    useEffect(() => {
      retrieveUserSettings().then(res => {
        let objRes = JSON.parse(res)
        setSelectedLanguage(objRes.language)
        setSelectedCurrency(objRes.currency)
        console.log(objRes, 'useEffect res')
      })
    }, [isFocused])

    useEffect(() => {
      dispatch(loadSettings())
    }, [])

    useEffect(() => {
      setSelectedCountry(userInfo.country)
    }, [userInfo])

    const retrieveUserSettings = async () => {
      return await AsyncStorage.getItem('userSettings')
    }

    return (
        <View style={styles.settingsScreen}>
           <View style={{paddingTop: insets.top, backgroundColor: '#0060C2'}}/>
            <AppBarLogo navigationLogo={()=>navigation.navigate('SearchScreen')}/>
            <ListLine label={'Country'} value={selectedCountry} onPress={()=>navigation.navigate(routes.selectCountry)}/>
            <ListLine label={'Language'} value={selectedLanguage} onPress={()=>navigation.navigate(routes.selectLanguage)}/>
            <ListLine label={'Currency'} value={selectedCurrency} onPress={()=>navigation.navigate(routes.selectCurrency)}/>
            <ListLine labelLong={'Terms and Confitions'} onPress={()=>navigation.navigate(routes.termsAndConditions)}/>
            <ListLine labelLong={'Privacy Policy'} onPress={()=>navigation.navigate(routes.privacyPolicy)}/>
            <ListLine labelLong={'Cookie Policy'} onPress={()=>navigation.navigate(routes.cookiePolicy)}/>
            <ListLine labelLong={'Notification Settings'} onPress={()=>navigation.navigate(routes.notificationSettings)}/>
        </View>
    )
}

export default SettingsScreen;

This is second:

const SelectCurrency = (props) => {
  const { navigation } = props;

  const currencies = useSelector(state => state.settingsReducer.data.currencies)
  const [selectedCurrency, setSelectedCurrency] = useState('Euro')

  useEffect(() => {
    retrieveUserSettings().then(res => setSelectedCurrency(JSON.parse(res).currency))
  }, [])

  useEffect(() => {
    saveUserSettings()
  }, [selectedCurrency])

  const retrieveUserSettings = async () => {
    return await AsyncStorage.getItem('userSettings')
  }

  const saveUserSettings = async() => {
    const storedData = await AsyncStorage.getItem('userSettings')
    const storedDataParsed = JSON.parse(storedData)
    const newData = { ...storedDataParsed, currency: selectedCurrency }
    await AsyncStorage.setItem('userSettings', JSON.stringify(newData))
  }

  const handleSelect = (currency) => () => {
    setSelectedCurrency(currency)
    navigation.goBack()
  }

  const renderList = () => {
    return Object.values(currencies).map((item, key) =>
    {
      return <TouchableOpacity
        style={styles.title_wrap}
        key={key}
        onPress={handleSelect(item.description)}>
        <View style={{...styles.title_wrap, marginBottom: 0, paddingHorizontal: 0}}>
          <Image style={styles.icon} source={{ uri: item.icon }}/>
          <Text style={styles.title}>{item.description}</Text>
        </View>
        <View style={{display: item.description === selectedCurrency ? 'flex' : 'none'}}><IconCheck/></View>
      </TouchableOpacity>
    })
  }

  return (
    <View style={styles.container}>
      <SafeAreaView style={styles.safeArea}/>
      <AppBarArrow navigation={()=>navigation.goBack()} header={'Select Currency'}/>
      {renderList()}
    </View>
  )
}

export default SelectCurrency

SelectCurrency screen and SelectLanguage is the same

this is screens how it looks with circled "labels", maybe it will help enter image description here


Solution

  • If you have the same issue this is probably not the best solution, but it works. Just modify useEffect that scopes on isFocused hook to this:

    useEffect(() => {
          const timer = setTimeout(() => {
            retrieveUserSettings().then(res => {
              let objRes = JSON.parse(res)
              setSelectedLanguage(objRes.language)
              setSelectedCurrency(objRes.currency)
            })
          }, 1);
          return () => clearTimeout(timer);
        }, [isFocused])