React Native鸿蒙跨平台开发AsyncStorage是提供的一个简单的、异步的、持久化的、key-value存储系统,非常适合存储小的数据片段,如用户的登录令牌、偏好设置等
在React Native中,实现本地数据存储有多种方式,每种方式有其特定的使用场景和优缺点。以下是几种常见的本地数据存储方式:
- AsyncStorage
AsyncStorage是React Native提供的一个简单的、异步的、持久化的、key-value存储系统。它非常适合存储小的数据片段,例如用户的登录令牌、偏好设置等。
使用示例:
import AsyncStorage from '@react-native-async-storage/async-storage';
// 存储数据
AsyncStorage.setItem('key', 'value')
.then(() => {
console.log('Data saved');
})
.catch(error => {
console.error('Error saving data', error);
});
// 读取数据
AsyncStorage.getItem('key')
.then(value => {
console.log('Data retrieved:', value);
})
.catch(error => {
console.error('Error retrieving data', error);
});
- SQLite 数据库
对于需要存储大量结构化数据的应用,可以使用SQLite数据库。React Native提供了react-native-sqlite-storage这样的库来操作SQLite数据库。
安装库:
npm install --save react-native-sqlite-storage
使用示例:
import SQLite from 'react-native-sqlite-storage';
SQLite.DEBUG(true); // 启用调试模式(可选)
var db = SQLite.openDatabase({name: 'mydb.db', location: 'default'}, () => {
console.log('Database opened');
}, (error) => {
console.error('Error opening database:', error);
});
db.transaction(tx => {
tx.executeSql('CREATE TABLE IF NOT EXISTS MyTable (id INTEGER PRIMARY KEY, data TEXT)');
tx.executeSql('INSERT INTO MyTable (data) VALUES (?)', ['test']);
});
- Realm Database
Realm是一个移动数据库,它提供了一个面向对象的编程接口,使得操作数据库更加简单和直观。它支持Harmony、Harmony和React Native。
安装库:
npm install realm @realm/react --save
使用示例:
import Realm from 'realm';
import {useRealm} from '@realm/react'; // 用于React hooks的集成
const MySchema = {
name: 'MyObject',
properties: {
id: 'int',
data: 'string'
},
primaryKey: 'id'
};
const realm = new Realm({schema: [MySchema]}); // 初始化Realm实例,并定义schema
const realmInstance = useRealm(realm); // 在React组件中使用Realm实例(如果你使用hooks)
- Redux Persist 或其他状态管理库结合 LocalStorage 或 AsyncStorage
如果你的应用使用了Redux进行状态管理,可以使用redux-persist库结合AsyncStorage或LocalStorage来持久化Redux的状态。这样,应用的状态就可以在应用重启后恢复。
安装库:
npm install redux-persist @react-native-async-storage/async-storage --save // 注意:@react-native-async-storage/async-storage 是 AsyncStorage 的替代品,用于React Native 0.60及以上版本。如果你使用的是旧版本,请使用 @react-native-community/async-storage。
配置示例:
import {persistStore, persistReducer} from 'redux-persist';
import storage from '@react-native-async-storage/async-storage'; // 或者使用 @react-native-community/async-storage 对于旧版本React Native。
import {createStore} from 'redux';
import rootReducer from './reducers'; // 你的reducer文件路径。
const persistConfig = {
key: 'root', // 存储的key值。可以根据需要更改。
storage, // 使用AsyncStorage作为存储介质。也可以使用其他支持的存储方式。例如:localStorage。对于React Native,推荐使用AsyncStorage。 如果你使用的是旧版本React Native,请使用AsyncStorage的社区版本。 例如:import AsyncStorage from '@react-native-community/async-storage'; 并且将storage设置为 AsyncStorage。 而不是'@react-native-async-storage/async-storage'。 这是因为'@react-native-async-storage/async-storage'是为React Native
真实案例演示代码:
// app.tsx
import React, { useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, TextInput, ScrollView, Alert, Image } from 'react-native';
const App = () => {
const [key, setKey] = useState('');
const [value, setValue] = useState('');
const [items, setItems] = useState<Record<string, string>>({});
const [editMode, setEditMode] = useState(false);
// Base64 icons
const icons = {
save: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM0Mjg1RjQiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMTkgMjFINGEyIDIgMCAwIDEtMi0yVjVhMiAyIDAgMCAxIDItMmg3Ij48L3BhdGg+PHBvbHlsaW5lIHBvaW50cz0iMTcgMiA5LjUgMiA3IDcgMTcgNyAxNyAyIj48L3BvbHlsaW5lPjxsaW5lIHgxPSI5IiB5MT0iOSIgeDI9IjE1IiB5Mj0iOSI+PC9saW5lPjxsaW5lIHgxPSI5IiB5MT0iMTMiIHgyPSIxNSIgeTI9IjEzIj48L2xpbmU+PGxpbmUgeDE9IjkiIHkxPSIxNyIgeDI9IjE1IiB5Mj0iMTciPjwvbGluZT48L3N2Zz4=',
delete: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNFRTQ0M0EiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMTkgNlY1YTIgMiAwIDAgMC0yLTJIN2EyIDIgMCAwIDAtMiAydjEiPjwvcGF0aD48bGluZSB4MT0iOCIgeTE9IjkiIHgyPSIxNiIgeTI9IjkiPjwvbGluZT48cGF0aCBkPSJNMTkgNlYyMGEyIDIgMCAwIDEtMiAyaC04YTIgMiAwIDAgMS0yLTJWNiI+PC9wYXRoPjxyZWN0IHg9IjkiIHk9IjYiIHdpZHRoPSIxMiIgaGVpZ2h0PSIxOCIgcng9IjIiIHJ5PSIyIj48L3JlY3Q+PC9zdmc+',
edit: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM0Mjg1RjQiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMTcgM0EyIDIgMCAwIDAgMTUuNTg2IDMuNDY0TDMuNTg2IDE1LjQ2NEEyIDIgMCAwIDAgMyAyMWgyLjVhMSAxIDAgMCAwIC43MDctLjI5M2wxMi0xMkExIDIgMCAwIDAgMTcgM3oiPjwvcGF0aD48L3N2Zz4=',
clear: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM5OTkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMTggNiA2IDE4Ij48L3BhdGg+PHBhdGggZD0iTTE4IDE4IDYgNiI+PC9wYXRoPjwvc3ZnPg=='
};
const saveData = () => {
if (!key.trim() || !value.trim()) {
Alert.alert('错误', '键和值都不能为空');
return;
}
setItems(prev => ({
...prev,
[key]: value
}));
setKey('');
setValue('');
Alert.alert('成功', '数据已保存');
};
const deleteItem = (itemKey: string) => {
setItems(prev => {
const newItems = { ...prev };
delete newItems[itemKey];
return newItems;
});
Alert.alert('成功', '数据已删除');
};
const clearAll = () => {
setItems({});
Alert.alert('成功', '所有数据已清空');
};
const loadItem = (itemKey: string, itemValue: string) => {
if (editMode) {
setKey(itemKey);
setValue(itemValue);
}
};
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>本地数据存储</Text>
<Text style={styles.subtitle}>简易键值对存储系统</Text>
</View>
<View style={styles.formContainer}>
<View style={styles.inputGroup}>
<Text style={styles.label}>键 (Key)</Text>
<TextInput
style={styles.input}
value={key}
onChangeText={setKey}
placeholder="输入键名"
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>值 (Value)</Text>
<TextInput
style={[styles.input, styles.textArea]}
value={value}
onChangeText={setValue}
placeholder="输入值"
multiline
numberOfLines={3}
/>
</View>
<View style={styles.buttonRow}>
<TouchableOpacity style={[styles.button, styles.saveButton]} onPress={saveData}>
<Image source={{ uri: icons.save }} style={styles.buttonIcon} />
<Text style={styles.buttonText}>保存</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.editButton]}
onPress={() => setEditMode(!editMode)}
>
<Image source={{ uri: icons.edit }} style={styles.buttonIcon} />
<Text style={styles.buttonText}>
{editMode ? '取消编辑' : '编辑模式'}
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.dataContainer}>
<View style={styles.dataHeader}>
<Text style={styles.dataTitle}>已存储的数据</Text>
{Object.keys(items).length > 0 && (
<TouchableOpacity style={styles.clearButton} onPress={clearAll}>
<Image source={{ uri: icons.clear }} style={styles.clearIcon} />
<Text style={styles.clearText}>清空</Text>
</TouchableOpacity>
)}
</View>
{Object.keys(items).length === 0 ? (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}>暂无数据</Text>
</View>
) : (
<View style={styles.dataList}>
{Object.entries(items).map(([itemKey, itemValue]) => (
<View key={itemKey} style={styles.dataItem}>
<TouchableOpacity
style={styles.dataContent}
onPress={() => loadItem(itemKey, itemValue)}
>
<Text style={styles.itemKey}>{itemKey}</Text>
<Text style={styles.itemValue} numberOfLines={2}>{itemValue}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.deleteButton}
onPress={() => deleteItem(itemKey)}
>
<Image source={{ uri: icons.delete }} style={styles.deleteIcon} />
</TouchableOpacity>
</View>
))}
</View>
)}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f8fa',
padding: 20
},
header: {
alignItems: 'center',
marginBottom: 30,
paddingTop: 20
},
title: {
fontSize: 26,
fontWeight: 'bold',
color: '#2c3e50'
},
subtitle: {
fontSize: 14,
color: '#7f8c8d',
marginTop: 5
},
formContainer: {
backgroundColor: '#fff',
borderRadius: 16,
padding: 20,
marginBottom: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4
},
inputGroup: {
marginBottom: 20
},
label: {
fontSize: 16,
fontWeight: '600',
color: '#34495e',
marginBottom: 8
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 12,
padding: 14,
fontSize: 16,
backgroundColor: '#fafafa'
},
textArea: {
height: 80,
textAlignVertical: 'top'
},
buttonRow: {
flexDirection: 'row',
justifyContent: 'space-between'
},
button: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 14,
paddingHorizontal: 20,
borderRadius: 12,
flex: 1,
marginHorizontal: 5
},
saveButton: {
backgroundColor: '#4285F4'
},
editButton: {
backgroundColor: '#34A853'
},
buttonIcon: {
width: 20,
height: 20,
marginRight: 8
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600'
},
dataContainer: {
backgroundColor: '#fff',
borderRadius: 16,
padding: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4
},
dataHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 15
},
dataTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#2c3e50'
},
clearButton: {
flexDirection: 'row',
alignItems: 'center'
},
clearIcon: {
width: 18,
height: 18,
marginRight: 5
},
clearText: {
color: '#999',
fontSize: 14
},
emptyContainer: {
paddingVertical: 40,
alignItems: 'center'
},
emptyText: {
color: '#999',
fontSize: 16
},
dataList: {
marginTop: 10
},
dataItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 15,
borderBottomWidth: 1,
borderBottomColor: '#eee'
},
dataContent: {
flex: 1,
paddingRight: 10
},
itemKey: {
fontSize: 16,
fontWeight: '600',
color: '#34495e',
marginBottom: 5
},
itemValue: {
fontSize: 14,
color: '#7f8c8d',
lineHeight: 20
},
deleteButton: {
padding: 10
},
deleteIcon: {
width: 22,
height: 22
}
});
export default App;
这段React Native代码实现了一个本地数据存储管理系统的用户界面和交互逻辑,其核心原理基于React的状态管理和React Native的原生组件系统。从鸿蒙系统适配的角度深入分析,该代码体现了现代跨平台移动应用开发的核心理念。
代码通过useState钩子管理三个核心状态:key(键名)、value(值)和items(存储的键值对集合)。这种状态管理模式与鸿蒙系统的分布式数据管理理念高度契合,鸿蒙系统强调数据驱动的UI更新机制,而React的单向数据流正好满足这一需求。当用户在输入框中输入数据时,通过onChangeText回调函数实时更新组件状态,触发界面重新渲染,这种响应式更新机制在鸿蒙设备上能够提供流畅的用户体验。
saveData函数实现了数据的存储逻辑,通过展开运算符创建新的items对象,避免直接修改原状态,这符合React不可变数据的原则。在鸿蒙系统中,这种数据处理方式能够更好地适配其分布式数据管理机制,确保数据状态的一致性和可预测性。函数中包含的数据验证逻辑体现了良好的用户体验设计,通过Alert组件提供即时反馈,这种交互模式在鸿蒙系统的原生应用中也广泛使用。
deleteItem函数采用delete操作符从items对象中移除指定键值对,通过创建新对象的方式保持状态的不可变性。这种实现方式在鸿蒙系统的内存管理中具有优势,能够有效避免内存泄漏问题。clearAll函数通过设置空对象实现数据清空,体现了简洁高效的设计思想。
loadItem函数实现了编辑模式下的数据加载功能,当用户点击已存储的数据项时,将选中项的键值填充到输入框中。这种交互模式在鸿蒙系统的应用设计中很常见,体现了直观易用的用户界面设计理念。

UI布局采用ScrollView作为根容器,确保内容在小屏幕设备上的可滚动性,这在鸿蒙系统的多设备适配中至关重要。View组件作为布局容器,通过flex布局实现响应式设计,能够自适应不同尺寸的鸿蒙设备屏幕。
TextInput组件实现了用户输入功能,通过multiline属性支持多行文本输入,numberOfLines属性控制显示行数。在鸿蒙系统中,这种输入组件会自动适配系统的输入法和键盘布局,提供原生的输入体验。TouchableOpacity组件实现触摸交互,其内置的触摸反馈效果符合鸿蒙系统的交互设计规范。
样式系统采用StyleSheet.create方式定义,这种做法在鸿蒙环境中能够获得更好的性能表现。通过样式对象的组合应用,实现复杂的视觉效果。背景色#f5f8fa等设计细节体现了现代化的UI设计理念,与鸿蒙系统的设计语言保持一致。
数据展示区域通过Object.entries方法将键值对转换为数组进行遍历渲染,每个数据项包含内容展示区和删除按钮。这种列表渲染模式在鸿蒙应用中广泛使用,能够高效处理动态数据集合。
整体而言,该代码通过React Native的跨平台能力,充分利用了鸿蒙系统的原生特性,在保持代码一致性的同时,为用户提供接近原生应用的使用体验。代码结构清晰,状态管理合理,交互逻辑完善,体现了现代移动应用开发的最佳实践。
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)