How to save items with AsyncStorage in one screen and then display it in a different screen

Viewed 262

First screen is made to show the history of the saved items from the second screen.

Second screen is a QR CODE Scanner. Once the QR CODE has been scanned it shows a Modal with the information it got from the QR CODE. It also has a Button to save the information. The information that is saved is retrieved in the first screen to a FlatList.

The problem I got is that the information that is saved doesn't show up in the first screen. I only get this in the console.log [Error: [AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .remove method instead. Passed value: undefined Passed key: @QR

EDIT: I forgot to write that the AsyncStorage Key is exported on the top of the file so it can be imported in the FIRST SCREEN.

CODE: SECOND SCREEN WHERE I SAVE THE INFORMATION.

const [Link, setLink] = useState([]);

const onSuccess = e => {
    setModalVisible(true);
    console.log(e);
    const QRSave = setLink(e);
    storeQRCode(QRSave);
  };

const storeQRCode = QRSave => {
    const stringifiedQR = JSON.stringify(QRSave);

    AsyncStorage.setItem(asyncStorage, stringifiedQR).catch(err => {
      console.log(err);
    });
  };

<Button
   title="Save QR"
   onPress={() => {
   scanner.reactivate();
   storeQRCode();
   showToast();
 }}
/>

CODE: FIRST SCREEN WHERE THE HISTORY IS SHOWN OF THE SAVED INFORMATION.

const [Data, setData] = useState({});

  useEffect(() => {
    restoreQRCode();
  }, []);

  const restoreQRCode = () => {
    AsyncStorage.getItem(asyncStorage)
      .then(stringifiedQR => {
        const parsedQR = JSON.parse(stringifiedQR);
        console.log(stringifiedQR);
        if (!parsedQR || typeof parsedQR !== 'object') return;

        setData(parsedQR);
      })
      .catch(err => {
        console.log(err);
      });
  };

<FlatList
  data={Data}
  keyExtractor={(_, index) => index.toString()}
  renderItem={renderItem}
 />
2 Answers

I have alternative solution without passing the saved information to previous screen

there is focus listener navigation event, the code is like this

useEffect(() => {
  const unsubscribe = navigation.addListener('focus', () => {
    // do something when this screen back to focus for example you can load data again whenever that screen focus...

    console.log('Screen X is focus again')
    loadData()
  })

  return unsubscribe
}, [navigation])

You can read more about this navigation events in this link

What you want is a context provider surrounding both screens, then you've got access to the data in both.

Something like:

//QrCodeContext.js
export const QrCodeContext = createContext();

const KEY = "QR";
export const QrCodeProvider = ({ children }) => {
    const [loading, setLoading] = useState(true);
    const [qrCode, setQrCode] = useState({});

    //This effect hook runs on mount and loads your QR code, if it exists.
    useEffect(() => {
        const loadQrCode = async () => {
            const code = await loadItemFromAsyncStorage(KEY, {});
            setQrCode(() => code);
            setLoading(() => false);
        };
        loadQrCode();

    }, [])

    //This effect hook runs every time the code changes and persists it to async storage
    useEffect(() => {
        const storeQrCode = async (value) => {
            await storeItemInAsyncStorage(KEY, value);
        }

        storeQrCode(qrCode);

        return () => {
            storeQrCode(qrCode);
        } // this means the code will always be saved when the component unmounts.

    }, [qrCode]);

    //These three properties are available to your child components.
    const propertiesExposedToConsumers = { setQrCode, qrCode, loading };

    return (<QrCodeContext.Provider value={propertiesExposedToConsumers}>
        {children}
    </QrCodeContext.Provider>
    );
}



//asyncStrageWrapper.js.

export const storeItemInAsyncStorage = async (key, value) => {
    try {
        const jsonValue = JSON.stringify(value);
        await AsyncStorage.setItem(key, jsonValue);
    } catch (e) {
        console.log(e);
    }
};

export const loadItemFromAsyncStorage = async (key, defaultValue = undefined) => {
    try {
        const jsonValue = await AsyncStorage.getItem(key);
        return jsonValue != null ? JSON.parse(jsonValue) : defaultValue;
    } catch (e) {
        console.log(e);
        return defaultValue;
    }
};


//screenOne.js

const ScreenOne = () => {
    const { loading, qrCode } = useContext(QrCodeContext);

   //your render code here
}

//screenTwo.js
const ScreenTwo = () => {
    const { loading, setQrCode } = useContext(QrCodeContext);
    //your scanner code here.
}

//App.js
const App = () => (
    <QrCodeProvider>
       <NavigationContainer>
      <Stack.Navigator initialRouteName={"ScreenOne"}>
      <Stack.Screen name="ScreenOne" component={ScreenOne} />
        <Stack.Screen name="ScreenTwo" component={ScreenTwo} />
      </Stack.Navigator>
    </NavigationContainer>
    </QrCodeProvider >
)
Related