【发布时间】:2020-06-03 16:33:55
【问题描述】:
有没有办法订阅AsyncStorage 的值更改?我在一个地方有一个设置,它保存在应用程序的AsyncStorage 中,它会影响其他所有屏幕。我需要观察该值,以便可以更新所有屏幕。我尝试了getValue 方法,但它最初似乎只获得一个值,并且不会在更改时更新。
【问题讨论】:
有没有办法订阅AsyncStorage 的值更改?我在一个地方有一个设置,它保存在应用程序的AsyncStorage 中,它会影响其他所有屏幕。我需要观察该值,以便可以更新所有屏幕。我尝试了getValue 方法,但它最初似乎只获得一个值,并且不会在更改时更新。
【问题讨论】:
我通过构建一个同步到 AsyncStorage 的 React Context 在我的应用中解决了这个问题。
在您的情况下,您可以有一个名为 appSettings 的项目。这将是一个包含所有应用设置的字符串化 JSON 对象。
const AppSettingsContext = React.createContext({})
const AppSettingsContextProvider = ({children}) => {
const [appSettingsInitialized, setAppSettingsInitialized] = useState(false)
const [appSettings, setAppSettings] = useState({}) // could use some default values instead of empty object
// On mount, get the current value of `appSettings` in AsyncStorage
useEffect(() => {
AsyncStorage
.getItem('appSettings')
.then(data => {
// If data is returned, the storage item existed already and we set the appSettings state to that.
if (data) {
setAppSettings(JSON.parse(data))
}
// If data is null, the appSettings keeps the default value (in this case an empty object)/
// We set appSettingsInitialized to true to signal that we have successfully retrieved the initial values.
setAppSettingsInitialized(true)
})
}, [])
// setSettings sets the local state and AsyncStorage
const setSettings = (key, value) => {
const mergedSettings = {
...appSettings,
[key]: value
}
// First, merge the current state with the new value
setAppSettings(mergedSettings)
// Then update the AsyncStorage item
AsyncStorage.setItem('appSettings', JSON.stringify(mergedSettings))
}
return (
<AppSettingsContext.Provider value={{
appSettings,
appSettingsInitialized,
setSettings,
}}>
{children}
</AppSettingsContext.Provider>
)
}
(请注意,这是一个非常基本的版本,没有错误处理)
然后将你的应用包装在 AppSettingsContextProvider 中
const App = () => (
<AppSettingsContextProvider>
{/* Other components */}
</AppSettingsContextProvider>
)
然后使用任何子组件的上下文:
const SomeChildComponent = () => {
const { appSettingsInitialized, appSettings } = useContext(AppSettingsContext)
// For example: wait until initial appSettings have been retrieved AsyncStorage,
// then use some value that you expect to be present in your business logic.
// In this case, setAppTheme would set the them color for the whole app, using a
// `theme` setting saved by the user.
useEffect(() => {
if (appSettingsInitialized) {
setAppTheme(appSettings.theme)
}
}, [appSettingsInitialized])
// Update settings like this for example
const updateThemeSetting = (newThemeValue) => {
setSettings('theme', newThemeValue) // eg 'dark', 'light', etc
}
}
【讨论】:
当然,如果你使用react-native-easy-app帮助你使用AsyncStorage,你可以通过react-native-easy-app获得两个好处,
第一:可以同步快速访问AsyncStorage,支持直接访问字符串、布尔值、对象、数组等数据code snippets
第二:当你修改任何AsyncStorage数据时,都会有一个回调函数告诉你相应的数据变化。 code snippets
详情请参考sample project
您可以按如下方式使用它:
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
// or import AsyncStorage from '@react-native-community/async-storage';
export const RNStorage = {
token: undefined,
isShow: undefined,
userInfo: undefined
};
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [console.log(await AsyncStorage.getItem('isShow'))]
console.log(RNStorage.isShow);
// equal to [ await AsyncStorage.setItem('token',TOKEN1343DN23IDD3PJ2DBF3==') ]
RNStorage.token = 'TOKEN1343DN23IDD3PJ2DBF3==';
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
const dataSetChangedCallback = (data) => {
data.map(([keyStr, value]) => {
let [, key] = keyStr.split('#');
console.log('data has changed:', key, '<###>', value);
})
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback, dataSetChangedCallback);
【讨论】:
您可以在 useEffect 中使用 AsyncStorage.getItem() 在组件挂载后立即查看值。
useEffect(() => {
(async () => {
console.log(await AsyncStorage.getItem('YOUR_ITEM_KEY'));
})();
}, []);
使用此语法,您可以使用键“YOUR_ITEM_KEY”记录项目。
如果你使用的是类组件,你可以这样做:
show = async () => {
console.log(await AsyncStorage.getItem('YOUR_ITEM_KEY'));
}
componentDidMount(){
this.show();
}
【讨论】:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app