如何在React Native中用Redux操作sqlite-storage/AsyncStorage及编写action creator
当然可以用Redux来处理本地数据库的存储流程,拆分STARTED/SUCCESS/FAIL状态不仅是合理的,还能让你在UI层更精准地展示数据存储的状态(比如加载中提示、成功/失败反馈)。下面我会一步步带你把你的SQLite存储逻辑迁移到Redux中,同时也会说明AsyncStorage的处理思路。
第一步:准备Redux基础(Action Types & Middleware)
首先你需要确保Redux项目中安装了redux-thunk中间件,因为数据库操作是异步的,我们需要用它来处理异步action。
先定义action类型常量:
// actionTypes.js export const SAVE_FOOD_DATA_STARTED = 'SAVE_FOOD_DATA_STARTED'; export const SAVE_FOOD_DATA_SUCCESS = 'SAVE_FOOD_DATA_SUCCESS'; export const SAVE_FOOD_DATA_FAILED = 'SAVE_FOOD_DATA_FAILED';
第二步:编写Action Creators
我们把原来的SQLite创建表、循环插入数据的逻辑放到一个异步action creator里(借助redux-thunk),在不同阶段dispatch对应的状态action:
// foodActions.js import { SAVE_FOOD_DATA_STARTED, SAVE_FOOD_DATA_SUCCESS, SAVE_FOOD_DATA_FAILED } from './actionTypes'; // 异步Action Creator:处理SQLite存储逻辑 export const saveFoodDataToSQLite = (categoryArr, nutrition) => { return (dispatch) => { // 第一步:dispatch开始状态,通知UI加载中 dispatch({ type: SAVE_FOOD_DATA_STARTED }); // 执行你的SQLite操作 db.transaction( function(txn) { txn.executeSql( "SELECT name from sqlite_master WHERE type='table' AND name='table_food'", [], function(tx, res) { console.log(`table_food exists: ${res.rows.length > 0}`); if (res.rows.length === 0) { txn.executeSql('DROP TABLE IF EXISTS table_food', []); txn.executeSql( "CREATE TABLE IF NOT EXISTS table_food(m_id INTEGER PRIMARY KEY AUTOINCREMENT, m_name VARCHAR(50), m_unit VARCHAR(250), cat_id VARCHAR(5), m_cal VARCHAR(6))", [], (tx, results) => { // 循环插入数据,用Promise统一处理异步插入 let insertPromises = []; for (var i = 0; i < categoryArr.length; ++i) { var cat_id = categoryArr[i].id; for (var j = 0; j < nutrition[cat_id].length; ++j) { var m_name = nutrition[cat_id][j].name; var m_unit = nutrition[cat_id][j].unit; var m_cal = nutrition[cat_id][j].cal; // 修正原代码的字段错误 // 把每个插入操作包装成Promise insertPromises.push( new Promise((resolve, reject) => { tx.executeSql( "INSERT INTO table_food(m_name, m_unit, cat_id, m_cal) VALUES (?,?,?,?)", [m_name, m_unit, cat_id, m_cal], (tx, results) => resolve(results), (tx, error) => reject(error) ); }) ); } } // 等待所有插入完成后dispatch成功状态 Promise.all(insertPromises) .then((results) => { dispatch({ type: SAVE_FOOD_DATA_SUCCESS, payload: { insertedCount: results.length } }); alert(`成功插入${results.length}条数据`); }) .catch((error) => { dispatch({ type: SAVE_FOOD_DATA_FAILED, payload: { error: error.message } }); alert(`插入数据出错:${JSON.stringify(error)}`); }); }, (error) => { dispatch({ type: SAVE_FOOD_DATA_FAILED, payload: { error: error.message } }); alert(`创建表出错:${JSON.stringify(error)}`); } ); } else { // 如果表已存在,可选择更新数据或直接标记成功 dispatch({ type: SAVE_FOOD_DATA_SUCCESS, payload: { message: '表已存在,无需重新创建' } }); } }, (error) => { dispatch({ type: SAVE_FOOD_DATA_FAILED, payload: { error: error.message } }); alert(`检查表存在性出错:${JSON.stringify(error)}`); } ); }, (error) => { dispatch({ type: SAVE_FOOD_DATA_FAILED, payload: { error: error.message } }); alert(`事务出错:${JSON.stringify(error)}`); } ); }; };
这里做了几个实用优化:
- 用
Promise.all统一处理所有插入操作,避免异步逻辑混乱 - 修正了原代码中
m_cal字段的赋值错误 - 每个错误分支都dispatch失败状态,确保store能捕获到错误信息
第三步:编写Reducer处理状态
接下来编写reducer来处理这三个状态,更新store中的数据:
// foodReducer.js import { SAVE_FOOD_DATA_STARTED, SAVE_FOOD_DATA_SUCCESS, SAVE_FOOD_DATA_FAILED } from './actionTypes'; const initialState = { isLoading: false, error: null, saveResult: null }; export default function foodReducer(state = initialState, action) { switch (action.type) { case SAVE_FOOD_DATA_STARTED: return { ...state, isLoading: true, error: null, saveResult: null }; case SAVE_FOOD_DATA_SUCCESS: return { ...state, isLoading: false, saveResult: action.payload }; case SAVE_FOOD_DATA_FAILED: return { ...state, isLoading: false, error: action.payload.error }; default: return state; } }
第四步:在组件中使用这个Action
当你从API拿到categoryArr和nutrition数据后,就可以dispatch这个action了:
// 你的组件文件 import { useDispatch, useSelector } from 'react-redux'; import { saveFoodDataToSQLite } from './foodActions'; function YourComponent() { const dispatch = useDispatch(); const { isLoading, error, saveResult } = useSelector(state => state.food); // 假设你已经从API获取到了categoryArr和nutrition const handleSaveData = () => { dispatch(saveFoodDataToSQLite(categoryArr, nutrition)); }; return ( <> {isLoading && <Text>正在保存数据...</Text>} {error && <Text style={{color: 'red'}}>保存失败:{error}</Text>} {saveResult && <Text>保存成功:{saveResult.message || `插入${saveResult.insertedCount}条数据`}</Text>} <Button title="保存到本地数据库" onPress={handleSaveData} /> </> ); }
关于AsyncStorage的处理思路
如果是用AsyncStorage存储,逻辑几乎一致:
- 把SQLite的事务、executeSql换成
AsyncStorage.setItem或者AsyncStorage.multiSet - 同样在异步action creator中处理STARTED/SUCCESS/FAIL状态
比如一个简单的AsyncStorage action:
export const saveFoodDataToAsyncStorage = (foodData) => { return (dispatch) => { dispatch({ type: SAVE_FOOD_DATA_STARTED }); AsyncStorage.setItem('foodData', JSON.stringify(foodData)) .then(() => { dispatch({ type: SAVE_FOOD_DATA_SUCCESS, payload: { message: '数据保存成功' } }); }) .catch((error) => { dispatch({ type: SAVE_FOOD_DATA_FAILED, payload: { error: error.message } }); }); }; };
要不要用Redux处理本地存储?
这取决于你的场景:
- 推荐用Redux的情况:如果这个本地存储操作的状态需要在多个组件中共享(比如多个页面需要知道数据是否正在保存、是否保存成功),或者你需要统一管理应用的状态逻辑,那么用Redux是非常合适的。
- 可以不用的情况:如果只是单个组件内部的本地存储操作,不需要和其他组件共享状态,那么直接在组件中处理即可,没必要引入Redux。
但就你的场景来说,你已经在用Redux管理API数据,把本地存储逻辑也纳入Redux能让状态管理更统一,是个不错的选择。
内容的提问来源于stack exchange,提问作者Neha
相关产品推荐
相关产品推荐

