You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:37:56