Home > Blockchain >  React.js - AsyncStorage: Have to press button twice to save the correct value i want
React.js - AsyncStorage: Have to press button twice to save the correct value i want

Time:10-13

I am trying to create a button that when i click it it will be disabled and when i reload the app it will stay disabled , until i press clear.

The problem is i have to click twice to get the result i want. Here is my code:

import React, {useState, useEffect} from 'react';
import {
  View,
  TouchableOpacity,
  Button,
  Text,
} from 'react-native';

import AsyncStorage from '@react-native-async-storage/async-storage';

export const BtnSave = () => {
    const [isChecked, setIsChecked] = useState(false);

    useEffect(() => { getButtonOption() }, [])


      const getButtonOption = async () => {
        try {
          const jsonValue = await AsyncStorage.getItem('buttonCheck', )
          const value = JSON.parse(jsonValue)
            setIsChecked(value);
        } catch(e) {
          console.log(e)
        }
      }

      //Execute when i click the button
      const buttonActions = async () => {
        
        setIsChecked(previousValue => !previousValue)
        buttonSaveClicked();
      };

      const buttonSaveClicked = async () => {
        let btnValue = isChecked.toString();
        //Have to click twice to change the value 
        console.log(`btn value is: ${btnValue}`)
        await AsyncStorage.setItem('buttonCheck',btnValue )
      };
    
    return  (
        <TouchableOpacity  >

      <View>
        <Button  title='save settings' onPress={buttonActions}></Button>
      </View>
    </TouchableOpacity>

    )
}

CodePudding user response:

The problem is with the getButtonOption function, since the key does not exist yet in the asyncstorage, it returns a null. Hence your isChecked is null on the first press. A very simple fix is to check if the value that is being retrieved on first load is a null value or not. Then set the correct state from there.

  const getButtonOption = async () => {
    try {
      const jsonValue = await AsyncStorage.getItem('buttonCheck');
      const value = JSON.parse(jsonValue);
      value ? setIsChecked(value) : setIsChecked(false);
    } catch (e) {
      console.log(e);
    }
  };

CodePudding user response:

I'm not sure if I totally understood your question but I can see some improvements that could help you and maybe it'll solve what you want.

Not parsing correctly the received JSON

I noticed that your function from parsing isn't considering the value as Boolean and you are trying to set a string 'true' or 'false' (string) to the value isChecked, that you expect to be true or false (booleans).

Remember that if you expect a boolean value and pass a non-empty string to it, it'll always evaluate true!

A simple change could fix that when saving:

// saving
AsyncStorage.setItem("buttonCheck", isChecked ? "true" : "false")
// loading
const jsonValue = AsyncStorage.setItem("buttonCheck")
const { buttonCheck } = JSON.parse(jsonValue);
seIsChecked(buttonCheck === "true")

No awaiting inside your buttonActions() function

I also noticed that you forgot to await the response from buttonSaveClicked() inside your buttonActions() function. I think changing the value from AsyncStorage inside this callback is a bad practice and provided one approach that I consider better later at this same response.

Calling an async function inside useEffect() from outside context

If you want to call an async function inside a useEffect() callback, make that function inside of the useEffect() callback such as (this article has some context about it):

useEffect(() => {
  const getButtonOption = async () => {
    try {
      const jsonValue = await AsyncStorage.getItem('buttonCheck');
      const { buttonCheck } = JSON.parse(jsonValue);
      setIsChecked(buttonCheck === "true");
    } catch(e) {
      console.log(e);
    }
  }

  getButtonOption();
}, [])

Reactively calling AsyncStorage.setItem() inside another useEffect()

You could take advantage of the reactive behavior of ReactJS and change the AsyncStorage value when the local state is changed, inside another useEffect() hook directly. Something like this could work:

useEffect(() => {
  const saveToAsyncStorage = async () => {
    try {
      await AsyncStorage.setItem('buttonCheck', isChecked ? "true" : "false");
    } catch (e) {
      console.log(e);
    }
  }

  saveToAsyncStorage();
}, [isChecked]);

I hope this could help you to solve your code!

  • Related