I have a project in React Native that utilizes React Navigation as well as Asyncstorage in order to store data to be edited by child screens. Since we need to listen for changes to each key globally, I made the following Asyncstorage hook that utilizes an EventEmitter:
import AsyncStorage from "@react-native-async-storage/async-storage";
import EventEmitter from "eventemitter3";
import { useEffect, useState } from "react";
const eventEmitter = new EventEmitter();
/**
* Acts as a react hook for AsyncStorage
* @param id - ID of the element
* @param defaultValue - Default value of the element
* @returns A react hook of the element
*/
export default function useStorage<Type>(id: string, defaultValue: Type): [Type, (value: Type) => Promise<void>] {
const [version, setVersion] = useState(0);
const [data, setData] = useState(defaultValue);
// Grab Data
const getData = async () => {
const jsonData = await AsyncStorage.getItem(id);
if (jsonData)
setData(JSON.parse(jsonData) as Type);
};
useEffect(() => { getData(); }, [version]);
// Save Data
const saveData = async (value: Type) => {
const jsonData = JSON.stringify(value);
await AsyncStorage.setItem(id, jsonData);
setData(value);
eventEmitter.emit(id);
}
useEffect(() => {
let isMounted = true;
const handleDataChange = () => {
if (isMounted) {
setVersion(v => v + 1);
}
}
const unmount = () => {
isMounted = false;
eventEmitter.removeListener(id, handleDataChange);
}
eventEmitter.addListener(id, handleDataChange);
return unmount;
}, [id]);
return [data, saveData];
}
However, whenever I utilize it, I get very strange results including...
Am I missing something? What is causing all of these issues? Is there a better way of implementing this?
Note: I am aware React Redux exists, but I would prefer to avoid it if possible.
Calling multiple setStates()
in a single function can cause unintended side effects. By adjusting the code to only use 1 setState()
per function, these issues can be avoided.