在React Native中,实现本地数据存储有多种方式,每种方式有其特定的使用场景和优缺点。以下是几种常见的本地数据存储方式:

  1. 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);
  });
  1. 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']);
});
  1. 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)
  1. Redux Persist 或其他状态管理库结合 LocalStorage 或 AsyncStorage

如果你的应用使用了Redux进行状态管理,可以使用redux-persist库结合AsyncStorageLocalStorage来持久化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工程目录去:

在这里插入图片描述

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

请添加图片描述

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

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

更多推荐