zoukankan      html  css  js  c++  java
  • react-native 之 redux 与 react-redux

    1.下载插件

    npm install redux --save
    
    npm install react-redux --save

    2.项目结构

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

    redux 必要知识

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

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

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

    因为 Action 是个对象,所以,我们需要创建这个对象,那创建这个对象的方法叫做 ActionCreator,写法:

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

    Reducerreducer 的作用就是根据传入的 Action行为和旧的 state对象,返回一个新的 state ,然后组件会根据 state 刷新。当我们确定了组件的 state 对象结构 和 action 行为的时候就可以编写 reducer 中的内容。写法:

    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 合成统一管理。

    import { combineReducers } from 'redux';
    import testReducer1 from './testReducer1';
    import testReducer2 from './testReducer2';
    
    export default = combineReducers({
        testReducer1,
        testReducer2
    });
    

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

    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

    .

  • 相关阅读:
    解决Maven项目 Missing artifact jdk.tools:jdk.tools:1.7的错误
    Hive三种不同的数据导出的方式
    【万字总结】图解堆算法、链表、栈与队列(多图预警)
    精心收集的Hadoop学习资料(持续更新)
    [大牛翻译系列]Hadoop 翻译文章索引
    sql server代理服务无法启动(SQL Agent):OpenSQLServerInstanceRegKey:GetRegKeyAccessMask failed (reason: 2).
    【转】sql server迁移到mysql
    linux shell中的EOF
    yum源出问题,rpmdb: BDB0113 Thread/process 17276/140338032428864 failed: BDB1507 Thread died in Berkeley DB library
    linux网卡出现问题:Job for network.service failed because the control process exited with error code问题
  • 原文地址:https://www.cnblogs.com/crazycode2/p/7469629.html
Copyright © 2011-2022 走看看