ReactNative

React Native: Redux 工程化实践

2019-01-31  本文已影响39人  Lin__Chuan

Redux 中文文档 在此. 如果想找具体可操作的案例, 文档里面都有. 文末有彩蛋.

为什么会有 Redux ?


在 iOS 中, 随着项目迭代, 功能越来越复杂, 如果还是采用 MVC 架构, 由于 Controller 内部的职责太多, 而导致代码块耦合严重, 不利于测试和维护, 由此, MVVM 应运而生.

MVVM 架构中, 通过将表现逻辑和交互逻辑移到 view-model 中, 借助 RxSwift 等响应式编程的框架, controller 监听 view-model 中的 view state 的变更, 而做出对应的操作, 比如修改 view.

协调器持有对 model 层的引用, 并且了解 view controller 树的结构, 这样, 它能够为每个场景的 view-model 提供所需要的 model 对象. 如果不增加协调器, 那么 view controller 间就会有耦合.
实际项目中是否引入协调器, 得看具体情况. 如果是针对不是那么复杂的功能做重构, 太复杂的架构反而是画蛇添足.


回到 React Native 项目, 如果 JavaScript 单页应用功能越来越复杂, 我们同样要处理功能模块解耦, 更细一点, 处理各种变化的 state. 这些 state 可能包括服务器响应数据, 缓存数据, 也包括 UI 状态, 如被选中的标签, 是否显示加载动效或者分页器等等.

所以我们选择了 Redux.

Redux 是什么 ?


ReduxJavaScrip 状态容器, 提供可预测化的 state 管理.

Redux 中, 这些 state , 也可以称之为 model 数据.
通过 action(交互逻辑, 显示逻辑), 更改不同的 state, 最后显示在界面上.

在下面代码中, POPULAR_REFRESHPOPULAR_REFRESH_SUCCESS 代表两种 action , 对于不同的 action, 内部需要传递的 state 数据也不同, 最终传递到 JavaScript 页面, 映射到 props 中, 做最后的处理.

case Types.POPULAR_REFRESH:   //下拉刷新中
    return {
        ...state, 
        [action.storeName]: {    // storeName 是类似于 java, ios等这些tab, 它是动态的
            ...state[action.storeName],
            refreshState: 1,
        }
    };
case Types.POPULAR_REFRESH_SUCCESS:   //下拉刷新成功
    return {
        ...state, 
        [action.storeName]: {
            ...state[action.storeName],
            items: action.items, //原始数据
            projectModels: action.projectModels,  // 此次要展示的数据
            refreshState: 0,    // 默认
            pageIndex: action.pageIndex
        }
    };

Redux 的工作流程

Redux 的工作流程

在整个流程中, 数据都是单向流动的.

Redux 的三原则
  1. Redux 应用中所有的 state 都以一个对象树的形式存储在一个 单一store 中.
  2. state只读 的: 唯一改变 state 的办法是触发 action, action 是一个描述发生什么的对象.
  3. 使用纯函数来执行修改: 为了描述 action 如何改变 state 树, 你需要编写 reducers.
    reducer 是形式为 (state, action) => state 的纯函数. 根据 action 修改 state, 将其转变为下一个 state.

Redux 在 React Native 中的应用


准备工作

根据需要, 安装以下组件.

安装方式

yarn add redux react-redux redux-devtools

react-redux 介绍

react-reduxRedux 官方提供的 React 绑定库.

有几个位置需要注意:

使用步骤

1. 创建 action

定义 action 类型

// 各种 action 类型
export const THEME_CHANGE = 'THEME_CHANGE'
export const POPULAR_REFRESH = 'POPULAR_REFRESH'

// 各种 action 类型
export default {
    THEME_CHANGE: "THEME_CHANGE", 
    POPULAR_REFRESH: "POPULAR_REFRESH"
}

创建 action 函数

import Types from '../types';

export function onThemeChange(theme) {
    // 同步 action
    // return {   
    //     type: Types.THEME_CHANGE,
    //     theme: theme,
    // }

    // 异步 action  需要引入 'redux-thunk'
    return dispatch => {
        dispatch({
            type: Types.THEME_CHANGE,
            theme: theme,
        })
    };
}

注意:

import thunk from 'redux-thunk'

const middlewares = [
    thunk,
    middleware2,
    middleware3,
];

export default createStore(reducers, applyMiddleware(...middlewares));
  • 默认情况下, createStore() 所创建的 Redux store 没有使用 middleware, 所以只支持同步数据流.

  • 我们可以使用 applyMiddleware() 来增强 createStore(), 添加 thunk 这类中间件来实现异步 action.

  • redux-thunkredux-promise 这类支持异步的 moddleware 都包装了 storedispatch() 方法. 因此我们可以 dispatch 一些除了 action 以外的内容. 例如函数或者 Promise.

  • 注意: 当 middleware 链中的最后一个 moddleware 开始 dispatch action 时, 这个 action 必须是一个普通对象.

2. 创建 reducers

reducer 是根据 action 类型 修改 state, 将其转变成下一个 state. 这里面根据实际的需要, 定义了各种不同的 state 树.

import Types from '../../action/types';

const defaultState = {
    theme: 'red'
}

export default function onAction(state=defaultState, action) {
    switch (action.type) {
        case Types.THEME_CHANGE:
            return {
                ...state,
                theme: action.theme,
            }
        default:
            return state;
    }
}

注意

import {combineReducers} from 'redux';

import theme from './theme';
import popular from './popular';

// 合并 reducer
const index = combineReducers({
    themeReducer: theme,
    popularReducer: popular,
})

export default index;

3. 使用 store

Store 是 存储 state 的容器.

store 里有几个方法

配置 store

import {createStore, applyMiddleware} from 'redux';
import reducers from '../reducer/reducer';
import thunk from 'redux-thunk'

const logger = store => next => action => {
    if (typeof action === 'function') {
        console.log('dispatching a function')
    } else {
        console.log('dispatching', action)
    }
    console.log('nextState', store.getState());
};

const middlewares = [
    logger,  // 打印 state 信息
    thunk,   // 提供异步 action
];

// 在 store 中添加中间件, 配置 reducer
export default createStore(reducers, applyMiddleware(...middlewares));

使用 store, 我们首先要引入 react-redux 库, 在 App 的根组件, 通过 <Provider/> 配置 store.

import {Provider} from 'react-redux';
import store from './js/store';

export default class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <AppNavigators />
      </Provider>
    )
  }
}

3. 在组件中应用 Redux

订阅 state, dispatch

import {connect} from 'react-redux';
import actions from '../action/index';

class HomePage extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Button 
          title='改变主题'
          onPress={()=> {
            this.props.onThemeChangeProp('orange')
          }}
        />
      </View>
    )
  }
}

const mapStateToProps = state => ({
  themeProp: state.themeReducer.theme
});

const mapDispatchToProps = dispatch => ({
  onThemeChangeProp: theme => dispatch(actions.onThemeChange(theme)),
});

export default connect(mapStateToProps, mapDispatchToProps)(HomePage);

在上述代码中, 我们订阅了 store 中的 theme state 和 dispatch,



react-navigation + Redux

如果你是通过 react-navigation 做 App 的基础架构, Redux 也对其做了支持.
我们需要导入 react-navigation-redux-helpers 库.

1. 配置 navigator

import {
    createAppContainer, createStackNavigator, createSwitchNavigator
}
from 'react-navigation';

import HomePage from '../page/HomePage';

import { connect } from "react-redux";
import {
    createReactNavigationReduxMiddleware, 
    createReduxContainer
}
from  "react-navigation-redux-helpers"

export const rootCom = 'HomePage';  // 设置根路由

const MainNavigator = createStackNavigator({
    HomePage: {
        screen: HomePage,
    }, 
}, {
    initialRouteName: rootCom
});

export const RootNavigator = createAppContainer(MainNavigator);

/**
 * 1. 初始化 react-navigation 与 redux 的中间件
 * 该方法的一个很大的作用是为 reduxifyNavigator 的 key 设置 actionSubscribers (行为订阅者)
 */
export const middleware = createReactNavigationReduxMiddleware(
    state => state.nav,
);

/**
 * 2. 将导航器传递给 reduxifyNavigator 函数
 * 并返回一个将 navigation state 和 dispatch 函数作为 props 的新组件
 * 注意: 要在 createReactNavigationReduxMiddleware 之后执行
 */
const AppWithNavigationState = createReduxContainer(RootNavigator);

/**
 * State 和 Props 的映射关系
 */
const mapStateToProps = state => ({
    state: state.nav  
})

/**
 * 连接 React 组件 与 Redux store
 */
export default connect(mapStateToProps)(AppWithNavigationState);

2. 配置 reducer

import {combineReducers} from 'redux';
import {createNavigationReducer} from 'react-navigation-redux-helpers'

import {RootNavigator} from '../navigator/AppNavigators';

import theme from './theme';
import popular from './popular';

// 1. 创建自己的 navigation reducer
const navReducer = createNavigationReducer(RootNavigator)

// 2. 合并 reducer
const index = combineReducers({
    nav: navReducer,
    themeReducer: theme,    // 子 reducer
    popularReducer: popular,// 子 reducer
})

export default index;

3. 使用 navigator

import {Provider} from 'react-redux';
import store from './js/store';

import AppNavigators from './js/navigator/AppNavigators';

export default class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <AppNavigators />
      </Provider>
    )
  }
}

至此, Redux 的基本使用都已经介绍完了.

Tips

enjoy :).

参考

如何理解 redux 的流程
react native redux 指南
官方中文文档

上一篇 下一篇

猜你喜欢

热点阅读