React Native中Redux-Saga配置指南:文件夹结构与代码拆分示例
我完全懂你这种多层传递props的烦躁!当初我刚接触React Native的时候,也被这种来回传token的操作搞得头大,选Redux-Saga绝对是个明智的选择——它把异步逻辑和UI逻辑拆得清清楚楚,维护起来特别省心。下面我就一步步给你讲怎么配置,还有针对Product和Firmware页面的代码拆分,结构跟你熟悉的Redux-Thunk差不多,很快就能上手~
第一步:先搭好文件夹结构
咱们按照模块拆分,每个页面(Product/Firmware)都有自己独立的actionTypes、actions、saga和reducer,结构清晰还不容易乱:
src/ ├── store/ │ ├── index.js # 核心store配置,整合saga和reducer │ ├── rootSaga.js # 合并所有子模块的saga │ ├── rootReducer.js # 合并所有子模块的reducer │ ├── product/ # Product专属模块 │ │ ├── actionTypes.js │ │ ├── actions.js │ │ ├── saga.js │ │ └── reducer.js │ └── firmware/ # Firmware专属模块 │ ├── actionTypes.js │ ├── actions.js │ ├── saga.js │ └── reducer.js └── components/ # 页面组件 ├── ProductScreen.js └── FirmwareScreen.js
第二步:逐个模块写代码
先从Product模块开始
1. 定义action类型(actionTypes.js)
// src/store/product/actionTypes.js export const FETCH_PRODUCTS_REQUEST = 'FETCH_PRODUCTS_REQUEST'; export const FETCH_PRODUCTS_SUCCESS = 'FETCH_PRODUCTS_SUCCESS'; export const FETCH_PRODUCTS_FAILURE = 'FETCH_PRODUCTS_FAILURE';
2. 写action创建函数(actions.js)
// src/store/product/actions.js import { FETCH_PRODUCTS_REQUEST, FETCH_PRODUCTS_SUCCESS, FETCH_PRODUCTS_FAILURE, } from './actionTypes'; // 请求开始的action export const fetchProductsRequest = () => ({ type: FETCH_PRODUCTS_REQUEST, }); // 请求成功的action export const fetchProductsSuccess = (products) => ({ type: FETCH_PRODUCTS_SUCCESS, payload: products, }); // 请求失败的action export const fetchProductsFailure = (error) => ({ type: FETCH_PRODUCTS_FAILURE, payload: error, });
3. 写saga处理异步逻辑(saga.js)
这里用axios发请求,记得带上存在AsyncStorage里的JWT Token:
// src/store/product/saga.js import { call, put, takeLatest } from 'redux-saga/effects'; import axios from 'axios'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { FETCH_PRODUCTS_REQUEST, fetchProductsSuccess, fetchProductsFailure, } from './actionTypes'; // 实际的API请求函数 const fetchProductsApi = async () => { const token = await AsyncStorage.getItem('jwtToken'); const response = await axios.get('https://你的接口地址/products', { headers: { Authorization: `Bearer ${token}` }, }); return response.data; }; // Saga工作函数:处理请求逻辑 function* fetchProducts() { try { const products = yield call(fetchProductsApi); // 调用API yield put(fetchProductsSuccess(products)); // 派发成功action } catch (error) { yield put(fetchProductsFailure(error.message)); // 派发失败action } } // Saga监听函数:监听请求action,避免重复请求 export function* watchFetchProducts() { yield takeLatest(FETCH_PRODUCTS_REQUEST, fetchProducts); }
4. 写reducer更新状态(reducer.js)
// src/store/product/reducer.js import { FETCH_PRODUCTS_REQUEST, FETCH_PRODUCTS_SUCCESS, FETCH_PRODUCTS_FAILURE, } from './actionTypes'; const initialState = { loading: false, products: [], error: '', }; const productReducer = (state = initialState, action) => { switch (action.type) { case FETCH_PRODUCTS_REQUEST: return { ...state, loading: true }; case FETCH_PRODUCTS_SUCCESS: return { loading: false, products: action.payload, error: '' }; case FETCH_PRODUCTS_FAILURE: return { loading: false, products: [], error: action.payload }; default: return state; } }; export default productReducer;
再写Firmware模块(和Product结构完全一致,替换业务内容即可)
1. actionTypes.js
// src/store/firmware/actionTypes.js export const FETCH_FIRMWARES_REQUEST = 'FETCH_FIRMWARES_REQUEST'; export const FETCH_FIRMWARES_SUCCESS = 'FETCH_FIRMWARES_SUCCESS'; export const FETCH_FIRMWARES_FAILURE = 'FETCH_FIRMWARES_FAILURE';
2. actions.js
// src/store/firmware/actions.js import { FETCH_FIRMWARES_REQUEST, FETCH_FIRMWARES_SUCCESS, FETCH_FIRMWARES_FAILURE, } from './actionTypes'; export const fetchFirmwaresRequest = () => ({ type: FETCH_FIRMWARES_REQUEST, }); export const fetchFirmwaresSuccess = (firmwares) => ({ type: FETCH_FIRMWARES_SUCCESS, payload: firmwares, }); export const fetchFirmwaresFailure = (error) => ({ type: FETCH_FIRMWARES_FAILURE, payload: error, });
3. saga.js
// src/store/firmware/saga.js import { call, put, takeLatest } from 'redux-saga/effects'; import axios from 'axios'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { FETCH_FIRMWARES_REQUEST, fetchFirmwaresSuccess, fetchFirmwaresFailure, } from './actionTypes'; const fetchFirmwaresApi = async () => { const token = await AsyncStorage.getItem('jwtToken'); const response = await axios.get('https://你的接口地址/firmwares', { headers: { Authorization: `Bearer ${token}` }, }); return response.data; }; function* fetchFirmwares() { try { const firmwares = yield call(fetchFirmwaresApi); yield put(fetchFirmwaresSuccess(firmwares)); } catch (error) { yield put(fetchFirmwaresFailure(error.message)); } } export function* watchFetchFirmwares() { yield takeLatest(FETCH_FIRMWARES_REQUEST, fetchFirmwares); }
4. reducer.js
// src/store/firmware/reducer.js import { FETCH_FIRMWARES_REQUEST, FETCH_FIRMWARES_SUCCESS, FETCH_FIRMWARES_FAILURE, } from './actionTypes'; const initialState = { loading: false, firmwares: [], error: '', }; const firmwareReducer = (state = initialState, action) => { switch (action.type) { case FETCH_FIRMWARES_REQUEST: return { ...state, loading: true }; case FETCH_FIRMWARES_SUCCESS: return { loading: false, firmwares: action.payload, error: '' }; case FETCH_FIRMWARES_FAILURE: return { loading: false, firmwares: [], error: action.payload }; default: return state; } }; export default firmwareReducer;
第三步:整合所有模块到Store
1. 合并所有saga(rootSaga.js)
// src/store/rootSaga.js import { all } from 'redux-saga/effects'; import { watchFetchProducts } from './product/saga'; import { watchFetchFirmwares } from './firmware/saga'; export default function* rootSaga() { yield all([ watchFetchProducts(), watchFetchFirmwares(), ]); }
2. 合并所有reducer(rootReducer.js)
// src/store/rootReducer.js import { combineReducers } from 'redux'; import productReducer from './product/reducer'; import firmwareReducer from './firmware/reducer'; const rootReducer = combineReducers({ product: productReducer, firmware: firmwareReducer, }); export default rootReducer;
3. 创建Store实例(index.js)
// src/store/index.js import { createStore, applyMiddleware } from 'redux'; import createSagaMiddleware from 'redux-saga'; import rootReducer from './rootReducer'; import rootSaga from './rootSaga'; // 创建saga中间件 const sagaMiddleware = createSagaMiddleware(); // 创建store并应用中间件 const store = createStore( rootReducer, applyMiddleware(sagaMiddleware) ); // 运行rootSaga sagaMiddleware.run(rootSaga); export default store;
第四步:在App中注入Store
在App.js里用Provider把整个应用包裹起来,让所有组件都能访问到Store:
// App.js import { Provider } from 'react-redux'; import store from './src/store'; import { NavigationContainer } from '@react-navigation/native'; // 导入你的页面和导航组件 import ProductScreen from './src/components/ProductScreen'; import FirmwareScreen from './src/components/FirmwareScreen'; export default function App() { return ( <Provider store={store}> <NavigationContainer> {/* 这里放你的导航栈,比如StackNavigator */} <ProductScreen /> <FirmwareScreen /> </NavigationContainer> </Provider> ); }
第五步:在页面组件中使用Store
以ProductScreen为例,用useDispatch派发action,useSelector获取状态:
// src/components/ProductScreen.js import React, { useEffect } from 'react'; import { View, Text, ActivityIndicator, StyleSheet } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { fetchProductsRequest } from '../store/product/actions'; const ProductScreen = () => { const dispatch = useDispatch(); // 从store中获取Product模块的状态 const { loading, products, error } = useSelector(state => state.product); // 页面加载时触发请求 useEffect(() => { dispatch(fetchProductsRequest()); }, [dispatch]); // 加载状态显示 if (loading) { return <ActivityIndicator size="large" color="#0066cc" style={styles.loader} />; } // 错误状态显示 if (error) { return <Text style={styles.errorText}>请求失败:{error}</Text>; } // 正常渲染产品列表 return ( <View style={styles.container}> {products.map(product => ( <Text key={product.id} style={styles.productItem}> {product.name} - {product.price}元 </Text> ))} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 16 }, loader: { flex: 1, justifyContent: 'center' }, errorText: { color: 'red', textAlign: 'center', marginTop: 20 }, productItem: { padding: 12, borderBottomWidth: 1, borderBottomColor: '#eee' }, }); export default ProductScreen;
FirmwareScreen的用法和上面完全一致,只需要替换对应的action和selector即可。
内容的提问来源于stack exchange,提问作者Reaksmey
相关产品推荐
相关产品推荐

