react-native 之 redux 与 react-redux

1.下载插件javascript

npm install redux --save

npm install react-redux --save

2.项目结构java

3.redux 适用于 多交互,多数据源,复杂程度高的工程中。react

redux 必要知识

使用 redux 以前,基本的东西仍是要都懂的,数据流向介绍:android

 

Action:行为。它的做用就是将咱们更新组件的 状态(state) 的每一个动做抽象为一个行为,它有一个必须的参数 type,定义了 Action(行为) 的名称,其余参数可自定义。写法:npm

{
    type: 'TEST_ACTION',   // 定义Action的名称
    key1: 'value',
        ...
    keyN: value
}

 

由于 Action 是个对象,因此,咱们须要建立这个对象,那建立这个对象的方法叫作 ActionCreator,写法:redux

function testAction(key1: ?string, ..., keyN: ?string) {
    return {
        type: "TEST_ACTION",
        key1: key1,
        ...
        keyN: keyN
    }
}

 

Reducerreducer 的做用就是根据传入的 Action行为和旧的 state对象,返回一个新的 state ,而后组件会根据 state 刷新。当咱们肯定了组件的 state 对象结构 和 action 行为的时候就能够编写 reducer 中的内容。写法:react-native

function testReducer(state, action) {
    let key1 = action.key1;
    switch(action.type) {
        case TEST_ACTION:
            return {
            ...state,
            key1: key1 + '变化'
        };

        default:
            return state;
    }
};

export default testReducer;

 

固然咱们的工程中可能会有多个 reducer 的状况,经过 combineReducers 能够将多个 reducer 合成统一管理。app

import { combineReducers } from 'redux';
import testReducer1 from './testReducer1';
import testReducer2 from './testReducer2';

export default = combineReducers({
    testReducer1,
    testReducer2
});

 

Store当 reducer 返回了新的 state 后,这个 state 怎么传到组件和存储就成了问题,redux 就是把这个状态统一放到 store 中进行管理。ide

import { createStore } from 'redux';
const store = createStore(reducers);

 

上面的代码根据 reducers 建立了一个 store方法集(它并非一个对象),而后再 store 中提供一些方法供咱们使用:函数

// 获取当前 state
store.getState()

// 发送action,根据咱们前面 注册的reducers 处理state
store.dispath(action)

// 替换当前 state 中的 reducer
store.replaceReducer(nextReducer) 

// 添加监听
store.subscribe(listener)

 

注:

另外 redux 有 5个 全局方法:

(1)createStore:建立一个redux store 来存储应用中全部的state,应用中只能存在一个 store

createStore(reducer, [initialState],enhancer);

(2)combineReducers:把多个reducer函数做为value的object,合并成一个reducers函数,而后就能够经过reducers调用各个子reducer,state 对象的结构由传入的多个 reducer 的 key 决定。

combineReducers(...reducers)

(3)...middlewares:每一个 middleware 接受 store 的 dispatch 和 getState 函数做为命名参数,并返回一个函数。

1.该函数会被传入被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接受 action 的新函数,这个函数能够直接调用 next(action),或者在其余须要的时刻调用,也可不调用。

2.调用链的最后一个 middleware 会接受真实的 store 的 dispatch 方法做为 next 参数,并结束调用链。因此 middleware 的函数为 ({ getState, dispatch }) => next => action。

3.返回值:一个应用了 middleware 后的 store enhancer。这个store enhancer 就是一个函数,而且须要应用到 createStore。它会返回一个应用了 middleware 的新 createStore。

(4)bindActionCreators:把 actionCreators 转曾拥有同名 keys 的对象,让 dispatch 把每一个 actionCreator 包装起来,这样就能够直接调用它们。惟一使用 bindActionCreators 的场景是须要把 actionCreator 往下传到一个组件上,却不想让这个组件察觉到 redux 的存在,并且不但愿把 redux store 或者 dispatch 传给它。

// actionCreators:一个 actionCreators 或 键值是 actionCreators 的对象
// dispatch:一个 dispatch 函数, 由 store 提供
bindActionCreators(actionCreators, dispatch)

返回值:一个与原对象相似的对象,只不过这个对象中的每一个函数值都直接 dispatch action。若是传入的是个函数,返回的也是函数。

(5)compose(...fuctions):当须要多个 store 加强器 依次执行的时候使用它。compose 在应用常见的两个用法:

// 1
let buildStore = compose(
    applymiddleware(thunk)
)(createStore)

// 2
let initStore = compose(
    applymiddleware(thunk)
)

参数1(arguments):合成多个函数。每一个函数接受一个函数做为参数,而后返回一个函数。

参数2(Function):从右往左把接受到的函数合成后的终极函数。

换张图理解:

 

react-redux 须要知道的那些事

react-redux 使用

(1)首先,根据 redux官方文档的示例 咱们能够看出官方建议咱们将组件分红 containers(容器组件)components(模块视图组件)redux 三大块。因此咱们这边文件的层级以下图所示:

adb version
adb devices

react-native init Test
cd Test
npm install --save redux
npm install --save react-redux
react-native start
react-native run-android

 

ipconfig
8081端口

 

(2)接着,咱们再来完成视图部分,而后根据视图部分肯定哪些须要 redux 支持,再来生成相应的 action 与 reducer 文件。

1.首先,是 Main 文件,做为咱们的容器组件放到 containers 文件夹内,Main 中的内容:

Main.js

/*主页面*/
import React, { Component } from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
} from 'react-native';

// 引入 测试组件
import TestText from '../components/TestText'
 
export default class Main extends Component {
    render() {
        return (
            <View style={styles.container}>
                {/* 须要改变的组件 */}
                <TestText />
                
                {/* 按钮 */}
                <TouchableOpacity>
                    <Text>改变文字按钮</Text>
                </TouchableOpacity>
            </View>
        );
    }
}
 
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
});

那里面咱们须要将 Text 做为视图组件独立出来,因此将视图组件 TestText 放到 components 文件夹中,TestText 中的内容:

TestText.js

/*测试组件*/
import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  View
} from 'react-native';
 
export default class TestText extends Component {
    render() {
        return (
            <Text>Welcome to React Native</Text>
        );
    }
}

(3)视图部分咱们搭建完成,那么咱们接着就是肯定须要哪些 action(行为),那前面提到了,咱们是要点击按钮的时候让文字发生改变,也就是说咱们当前须要一个改变文字的行为,那咱们就将这个行为命名为 CHANGE_TEXT,那么咱们须要初始化这个 action 这个对象,也就是前面咱们提到的 action creator:

action.js

/**
 * 步骤一
 * 行为 action
 */

// 定义行为名称
export const CHANGE_TEXT = 'CHANGE_TEXT';

// 初始化 CHANGE_TEXT 对象
export const changeText = (text) => { // 接收test参数
    return {
        type: CHANGE_TEXT, // 名称
        text // 参数 默认值
    }
};

(4)action 文件配置完毕后,咱们就能够根据需求来编写 reducer 文件了,reducer 文件就是起到更新 state 的做用嘛,因此咱们将改变 文字 的逻辑放到这里,当reducer 匹配到当前的点击行为为 CHANGE_TEXT 时,就执行相应的操做,返回一个新的 state 给咱们使用,若是匹配不到,那么就默认返回一个不变的新 state:

reducer.js

/**
 * 步骤二
 * 操做
 * 经过reducer操做action(根据action行为建立reducer文件)
 */

/**
 * 引入 action
 * CHANGE_TEXT 类型(行为名称)
 * changeText 值
 */
import { CHANGE_TEXT, changeText } from '../action/action';

/**
 * 建立 reducer
 * 根据名称判断是哪个行为
 * state = changeText('welcome to React Native') 初始化state
 */
const mainReducer = (state = changeText('welcome to React Native'), action) => {
 	/**
 	 * state 不能直接改变
 	 * 定义newState 接收state的值
 	 */
    const newState = state;
    const text = action.text;
 
    // 判断 action 类型
    switch (action.type) {
        case CHANGE_TEXT:
            return {
            	// 返回全部的newState
                ...newState,
                text: '改变了' + text
            };
 
        default:
            return {
                ...newState,
                text:state.text
            }
    }
};

// 输出口
export default mainReducer;

(5)配置完 action 和 reducer 两个文件后,紧接着咱们就能够根据 reducer 来初始化 store 了:

store.js

/**
 * 步骤三
 * 初始化 store
 */
// 引入 reducer(操做)
import Reducer from '../reducer/reducer';
// 获取redux中的初始化方法 createStore
import { createStore } from 'redux';

// 输出
export default () => {
 
    // 根据 reducer 初始化 store
    const store = createStore(Reducer);
 
    return store;
}

(6)redux 的东西已经都配置完成了,接着就剩下使用了,因此接下来要解决的问题就是怎么发送行为,怎么接收 state(状态),上面提到了,store 实际上是个方法集,咱们的 发送行为 和 接收状态 方法都在 store 中,因此只要拿到 store,因此只要拿到 store 就能进行这两个操做。

(7)那怎么拿到 store 呢?在官方文档中,清楚地告诉咱们,Provider 的任务就是将 store 传给 connect,而 connect 的做用是将咱们的组件进行第二次包装,将操做数据的函数和数据的状态包装到 props 中,因此,首先,咱们须要对咱们的 Main 文件进行第一次包装,咱们再新建一个 index 文件来对 Main 文件进行包装:

Index.js

/**
 * 容器组件
 * 入口文件
 */
import React, { Component } from 'react';
 
// 引用外部文件
import { Provider } from 'react-redux';
import Main from './Main';
import configureStore from '../redux/store/store';
 
// 调用 store 文件中的 mainReducer常量中保存的方法
const store = configureStore();
// 定义根组件
export default class Root extends Component {
    render() {
        return(
            // 第一层包装,为了让 main 可以拿到 store
            <Provider store={store}>
                <Main />
            </Provider>
        )
    }
}

(8)包装完成后,咱们的 Main 文件就能够得到 store 了,那接着就是进行第二次包装了,经过 connect 生成新组件:

Main.js

/*主页面*/
import React, { Component } from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
} from 'react-native';

// 引入 测试组件
import TestText from '../components/TestText'
/**
 * 获取 react-redux的 connect() 方法
 * 注:使组件层级中的 connect() 方法可以获得 redux store
 */
import { connect } from 'react-redux';
// 获取 action行为的值
// import { CHANGE_TEXT } from '../redux/action/action';
import { changeText } from '../redux/action/action';
 
class Main extends Component {
    render() {
        // 经过 props 拿到保存的 onChangeText
        const { onChangeText } = this.props;
        
        return (
            <View style={styles.container}>
                {/* 须要改变的组件 */}
                {/* 将父组件(Main)的props,传递给子组件(TestText)*/}
                <TestText {...this.props} />
                
                {/* 按钮 */}
                <TouchableOpacity
                    // 设置按钮点击事件
                    onPress={onChangeText}
                >
                    <Text>改变文字按钮</Text>
                </TouchableOpacity>
            </View>
        );
    }
}
 
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
});

/************ 初始化 ************/
// 获取 state 变化
const mapStateToProps = (state) => {
    return {
        // 获取 state 变化
        value: state.text,
    }
};
 
// 发送行为
const mapDispatchToProps = (dispatch) => {
    return {
        // 发送行为
        // onChangeText: () => dispatch({type: CHANGE_TEXT}),
        onChangeText: () => dispatch(changeText('外部传值')),
    }
};
 
/**
 * 经过 connect() 方法 对Main组件进行第二层包装
 * 进行第二层包装,生成的新组件拥有 接收和发送 数据的能力
 * mapStateToProps 获取状态的函数
 * mapDispatchToProps 发送行为的函数
 */
export default connect(mapStateToProps, mapDispatchToProps)(Main);

(9)到这里,咱们的 新组件 就可以收发数据了,那怎么接收和发送呢,别急,咱们接着就来完成 mapStateToProps(更新回调) 和 mapDispatchToProps(发送行为) 两个方法。首先,咱们须要经过 mapDispatchToProps 来发送行为,而后经过 mapStateToProps 来监听 state 的变化,这边咱们须要发送的行为 type 是 CHANGE_TEXT,当发送行为以后,reducer 就会去匹配 行为的类型,进行相应操做:

// 发送行为
const mapDispatchToProps = (dispatch) => {
    return {
        onChangeText: () => dispatch(changeText('外部传值')),
    }
};

(10)当 reducer 接收到咱们触发的 行为 并进行一系列处理后,最终会返回一个新的 state,那么 就会自动调用 mapStateToProps 来告诉系统,state 被操做了,那么咱们就能够经过 mapStateToProps 来获取 state 状态:

// 获取 state 变化
const mapStateToProps = (state) => {
    return {
        value: state.text,
    }
};

(11)那么接下来咱们 怎么改变文字 呢?前面提到,connect 做用就是生成一个新的组件,新的组件的 props 中包含了数据获取和操做数据的函数,因此咱们须要让 子组件拿到容器组件中的 props,而后在 子组件 中经过 props 就能够拿到上面 定义的 value 和 onChangeText:

TestText.js

/*测试组件*/
import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View
} from 'react-native';
 
export default class TestText extends Component {
    render() {
        // 获取 props 中的 value
        const { value } = this.props;
 
        return (
            // 根据 value 改变内部文字
            <Text>{value}</Text>
        );
    }
}

(12)效果图

注:转载 http://blog.csdn.net/yeshaojian/article/details/70599572

.