美文网首页
Redux概念

Redux概念

作者: 泡杯感冒灵 | 来源:发表于2022-03-19 15:12 被阅读0次
    React本身是一个轻量级的视图框架,如果只用react框架,做大型项目的时候,组件间的传值将会变得非常麻烦。这个时候,我们需要用引入数据管理框架Redux, Redux就是把组件中的数据,放到一个公用的存储区域去存储,然后当一个组件改变store里的数据的时候,其他组件会感知到store里数据的变化,然后重新去取store里的最新数据。Redux大大简化了数据的传递难度。
    image.png
    Redux = Reducer + Flux
    Redux的工作流程

    简单理解为以下流程:组件(React Components)告诉Store,我需要获取你里边的数据(Action Creators),但是到底该给组件什么数据呢?Store并不知道, 这个需要Reducers告诉Store应该给什么数据,Store知道了以后,把数据给组件就可以了。
    同样道理,当组件要修改Store里的数据的时候,需要先告诉Store,我要改变你里边的数据(Action Creators),但是Store不知道怎么去帮你改变里边的数据,它需要去问 Reducers,Reducers会告诉Store该怎么去修改数据,当Store改好数据以后,会通知组件,数据已经修改好了,你可以重新来获取数据了。这样组件就可以重新获取最新的数据了!

    image.png
    redux使用流程
    • 安装redux npm install --save redux
    • 创建一个store,具体方法是,src下,创建一个store文件夹,里边创建一个index.js文件,然后index.js里边引入redux里的 createStore方法,当然我们不能单单创建一个store,还需要在创建store的时候,传入一个reducer,reducer返回的必须是一个函数,这个函数接收两个参数,state和action,reducer负责管理整个应用里的一些数据,怎么处理,怎么存。
      image.png
      image.png
      第一次reducer第一次执行的时候,store传的state是没有数据的,所以准备了一个defaultState作为默认值,复制给state,然后再把这个state返回给store,所以defaultState实际上,就是store的默认值。
    具体使用流程,以todolist为例
    • 首先我们要实现input框输入的时候,store里的inputValue值跟着改变的功能。然后把store里的inputValue值放入store的list.
    // 第一步:input框上,要监听input的change事件,并绑定事件处理函数。
         <Input 
              value={this.state.inputValue}  
              placeholder="todo info" 
              style={{width:'300px',marginRight:'10px'}}
              onChange={this.handleInputChange}
         >
         </Input>
    
    // 第二步:当change事件触发的时候,创建一个action,action是一个对象。
    // action包含type,要做什么事(自定义名字),和要传的数据。
    // 然后再调用store的dispatch方法,把这个action传给store
        handleInputChange(e){
            const action = {
                type:'change_input_value',
                value:e.target.value
            }
            store.dispatch(action);
        }
    
    // 第三步:store本身是不对数据进行处理的,接收到组件传的action后,会把当前store里的内容和这个action,一起给到reducer。
    // reducer就能接受到store之前的内容(state)和传过来的action
    // reducer根据action的类型,对数据进行处理,然后把新的数据return出去,返回给store
    export default (state = defaultState, action) => {
        if(action.type === 'change_input_value'){
            // 注意 reducer可以接收state,但是决不能修改state,所以下边要对state进行深拷贝
            const newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        }
        if(action.type === 'add_todo_item'){
            const newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            console.log(newState);
            return newState;
        }
        return state;
    }
    
    // 第四步 store拿到最新的state之后,把之前的state替换掉
    
    // 第五步 组件通过store.subscribe方法监听store的变化,并触发绑定的函数
    // 在subscribe绑定过函数里,重新获取store里的最新的数据,并修改自己的state
        constructor(props){
            super(props);
            this.state = store.getState();
            this.handleInputChange = this.handleInputChange.bind(this);
            this.handleStoreChange = this.handleStoreChange.bind(this);
            this.handleBtnClick = this.handleBtnClick.bind(this);
            store.subscribe(this.handleStoreChange);
        }
        handleStoreChange(){
            this.setState(store.getState());
        }
    

    注意 reducer可以接收state,但是决不能修改state,所以要对state进行深拷贝

    ActionTypes的拆分
    • 当我们创建action的时候,会定义这个action的type,值是自定义的字符串,但是这里有个问题,当我们使用这个字符串进行action的判断的时候,如果字符串写错了,页面是不会报错的,不利于我们定位问题。这个时候,我们可以对action的type进行拆分,使用常量代替原来的字符串。
    // 先在store下创建 actionTypes文件,里边定义types的常量并导出
    export const CHANGE_INPUT_VALUE = 'change_input_value';
    export const ADD_TODO_ITEM = 'add_todo_item';
    export const DELETE_TODO_ITEM = 'delete_todo_item';
    
    
    // 在用到action的type的地方,引入刚才定义的常量
    import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM} from './store/actionTypes';
    
    // 使用的时候,直接用常量代替字符串
        handleBtnClick(e){
            const action = {
                type:ADD_TODO_ITEM
            }
            store.dispatch(action);
        }
    
    • 然后我们在使用过程中,如果不小心写错了变量某个字符,页面就会报错。
    使用actionCreators 统一创建 action
    • 比较简单功能,action分散在业务逻辑里边还可以,但是当业务逻辑非常复杂的情况下,action到处都是,对后期的测试和管理是非常不方便的。所以,我们要把action拆分到actionCreator 里进行统一的创建管理。
    // 在store下创建actionCreators.js文件,我们把action的创建统一放到这个文件里,对action进行统一的管理,
    // 主要是为了提高代码的可维护性,方便自动化测试
    import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM} from './actionTypes';
    
    // 定义一个常量,是一个函数,该函数返回一个对象,对象包含action的type和值
    export  const getInputChangeAction = (value) => ({
        type: CHANGE_INPUT_VALUE,
        value
    })
    
    export  const getAddItemAction = () => ({
        type: ADD_TODO_ITEM
    })
    
    export  const getDeleteItemAction = (index) => ({
        type: DELETE_TODO_ITEM,
        index
    })
    
    // 具体的使用,先引入这些actionCreators 里的action
    import {getInputChangeAction, getAddItemAction,getDeleteItemAction} from './store/actionCreators';
    
    // 然后使用store.dispatch去触发action
        handleInputChange(e){
            const action = getInputChangeAction(e.target.value);
            store.dispatch(action);
        }
        handleStoreChange(){
            this.setState(store.getState());
        }
        handleBtnClick(e){
            const action = getAddItemAction();
            store.dispatch(action);
        }
        handleItemDelete(index){
            const action = getDeleteItemAction(index);
            store.dispatch(action);
        }
    
    

    注意Redux的设计使用原则

    • 注意Redux的设计使用原则,第一个就是store必须是唯一的,也就是整个应用中只有一个store作为公用存储
    • 只有store能够改变自己的内容。(reducer返回了数据,store会用这个数据自己去替换自己原来的数据),换句话说这也是为什么reducer不能修改state的内容,state指的就是store里的数据
    • Reducer必须是纯函数(纯函数指的是,给定固定的输入,就一定会有固定的输出,而且不会有任何的副作用)。如果函数里有ajax请求,setTimeout等异步的时候或与日期相关的时候,就不是固定的输入了,所以Reducer里不能有异步的操作,也不能有跟时间相关的操作
    export default (state = defaultState, action) => {
        if(action.type === CHANGE_INPUT_VALUE){
            const newState = JSON.parse(JSON.stringify(state));
            // 如果inputValue = new Date().那么就不会是一个固定的值了。是不行的。
            newState.inputValue = action.value;
            // 如果把newState 换成state,那么就是改变了参数,而对reducer传入的参数的修改是有副作用的。
            state.inputValue = action.value;
            return newState;
        }
        if(action.type === ADD_TODO_ITEM){
            const newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState;
        }
        if(action.type === DELETE_TODO_ITEM){
            const newState = JSON.parse(JSON.stringify(state));
            newState.list.splice(action.index,1);
            return newState;
        }
        
        return state;
    }
    
    Redux-DevTools 是一个非常棒的工具,它可以让你实时的监控Redux的状态树的Store,在谷歌扩展程序安装后,要想开发环境下使用,还需配置。
    // 未配置之前,创建的store
    import { createStore} from "redux";
    import reducer from './reducer';
    
    const store = createStore(reducer);
    export default store;
    
    // 配置以后,创建是store
    import { createStore,applyMiddleware, compose } from "redux";
    import reducer from './reducer';
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const store = createStore(reducer, /* preloadedState, */ composeEnhancers());
    export default store;
    
    • 当这个图标变绿色就证明配置成了,可以调试了


      image.png
    总结Redux的核心API
    1. createStore 绑我们创建store
    2. store.dispatch方法,帮我们派发创建的action,这个action会传递给store
    3. store.getState方法,帮我们获取到store里的所有的数据内容
    4. store.subscribe方法,帮我们订阅store里数据的改变,只要store发生改变,subscribe方法接收的函数,就会被执行。
    如果说store是图书管理员,那么reducer就可以看作是图书记录手册,管理员需要通过记录手册来对数据进行操作。随着项目复杂度的增加,如果reduce存放过多的数据,可能造成代码的低可维护性,这个时候,我们就需要把一个reducer拆分成多个子的reducer,最后再做整合。具体怎么做呢?
    • 首先,创建子的reducer,比如我的Header文件夹里存放的都是Header组件的内容,那么这个时候,可以在Header文件夹下创建一个store文件夹,store文件夹下再创建一个reducer.js文件。

    • 然后在总的store文件夹下的reducer.js文件里,通过combineReducers方法进行整合。

    // 把一些小的reducer合并成大的reducer
    import { combineReducers } from 'redux';
    import headerReducer from '../common/header/store/reducer';
    
    export default combineReducers({
        // header是自定义的名字,可随意起名
        header:headerReducer
    })
    
    image.png
    • 整合以后,我们再取值的时候,就需要多一层。
    // 没用拆分之前
    const mapStateToProps = (state) => {
        return {
            focused:state.focused
        };
    }
    
    // 拆分后
    const mapStateToProps = (state) => {
        return {
            focused:state.header.focused
        };
    }
    
    • 当然了,像上边那样创建子reducer,在总的reducer引入子reducer的时候,层级有可能会很深。
    import headerReducer from '../common/header/store/reducer';
    
    • 这个时候,可以给子reducer创建一个出口文件,index.js,然后在出口文件里,把recucer导出


      image.png
    • 然后在总的reducer里引入的时候,就可以这样


      image.png

    相关文章

      网友评论

          本文标题:Redux概念

          本文链接:https://www.haomeiwen.com/subject/npnvdrtx.html