React--redux

195 阅读8分钟

Redux

redux 是 JavaScript 状态容器,提供可预测化的状态管理。 可以用在React、Vue等前端库中。 react-redux是react中的redux插件,简化了redux的用法,使开发者更方便地使用redux来管理状态。

redux: 进行状态统一管理的类库(适应于任何技术体系的项目).

应用场景

1.只要两个或者多个组件之间想实现信息的共享,都可以基于redux解决,把共享的信息存到redux容器中进行管理.
2.还可以使用redux做临时存储: 页面加载的时候,把从服务器获取的数据信息存储到redux中,页面组件渲染需要的数据,从redux中获取,这样只要页面不刷新,路由切换的时候,再次渲染组件不需要重新从服务器中拉取数据,直接从redux获取即可;页面刷新,从头开始

为什么要有redux?

react中的数据流是单项传递的,即状态只能从父组件传递给子组件,虽然可以用ref在父调中调用子组件的方法从而返回子组件的状态给父组件使用、或者使用contex跨级传递属性等方法,但是当组件很多而且都需要共享一个属性的时候,那些方法管理属性就显得不规范且不方便,因此,需要使用redux这个专门的状态容器来统一管理状态。

Redux 的设计思想

设计思想很简单,就两句话:

1、Web 应用是一个状态机,视图与状态是一一对应的。
2、所有的状态,保存在一个对象里面。

redux暴露的几个方法

createStore

createStore: 创建容器,需要把reducer传递进来。createStore返回四个方法: 

    -> dispatch: 派发行为(传递一个对象,对象中有一个type属性),目的是通知reducer修改状态信息
    -> subscribe: 给事件池追加方法
    -> getState: 获取最新管理的状态信息
    -> replaceReducer: 替换reducer(一般不用)
let store = createStore(reducer);

redux+hook实现n,m的++

import React,{ Component,useState } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';

/*
* reducer的作用:
    1.记录了所有状态修改的信息(根据不同的标识去操作和修改任务)
    2.修改容器中的状态信息
* reducer的参数: reducer(state,action);
    state: 原有的状态信息
    action: 任务派发(dispatch)的时候,传递的行为对象(这个对象中必须有一个type属性==操作的行为标识(reducer就是根据这个行为标识修改状态信息的))
* reducer一定得把state给return出去.(否则getState()执行的时候取不到)
*/ 
let reducer = (state={ n:0,m:0 },action)=>{
    switch(action.type){
        // CHANGE_N和CHANGE_M就是行为标识
        case 'CHANGE_N':
            state = {...state,n: state.n+1};
            break;
        case 'CHANGE_M':
            state = {...state,m: state.m+1}
            break;
        default:
            break;
    }
    return state;  //这里一定要把state返回.
}

// 1.创建容器,传入reducer
let store = createStore(reducer);


class App extends Component{

    changeM = ()=>{
        store.dispatch({
            type: 'CHANGE_N'
        })
    }
    
    changeN = ()=>{
        store.dispatch({
            type: 'CHANGE_M'
        })
    }

    render(){
        return(
            // {/* React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。 */}
            <React.Fragment>
                <ComN store={ store }></ComN>
                <ComM store={ store }></ComM>
                <ComBtn 
                    store={ store } 
                    changeM={ this.changeM } 
                    changeN={ this.changeN }>
                </ComBtn>
            </React.Fragment>
        )
    }
}

// 组件N
function ComN(props){
    let {store:{ getState,subscribe }} = props;
    let { n } = getState();
    
    // 使用useState创建函数中的状态信息
    let [nValue,setNValue] = useState(n);
    
    subscribe(()=>{
        let { n } = getState();
        setNValue(n);
    })
    
    return(
        <div>n的值: { nValue } </div>
    )
}


// 组件M
function ComM(props){
    
    let { store:{ getState,subscribe} } = props;
    let { m } = getState();
    
    let [mValue,setMValue] = useState(m);
    
    subscribe(()=>{
        let { m } = getState();
        setMValue(m);
    })
    
    return(
        <div>m的值: { mValue } </div>
    )
}

// 改变N,M值的组件
function ComBtn(props){
    let btn = {
        marginTop: '30px',
        display: 'inline-block',
        width: "100px",
        height: "40px",
        border: "1px solid #ccc",
        textAlign: 'center',
        lineHeight: '40px',
    }
    return(
        <div>
            <div style={ btn } onClick={ ()=>{  
                props.changeN() } }>改变 n++</div>
            <div style={ btn } onClick={ ()=>{  
                props.changeM() } }>改变 m++</div>
        </div> 
    )
}
ReactDOM.render(<App />,document.getElementById('root'));

combineReducers

combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。

import { combineReducers } from 'redux';
import reducerA from './reducerA';
import reducerB from './reducerB';

const reducer = combineReducers({
    reducerA,
    reducerB
});

export default reducer;

bindActionCreators

先看这里

applyMiddleware

应用中间件applyMiddleware

import { createStore,applyMiddleware } from 'redux';
import reducer from './reducer';
import reduxLogger from 'redux-logger';
import reduxThunk from 'redux-thunk';
import reduxPromise from 'redux-promise'
// 应用中间件applyMiddleware
let store = createStore(reducer,applyMiddleware(reduxLogger,reduxThunk,reduxPromise));

compose

先看这里

redux工程化项目目录结构

-> store
    -> index.js                 // 创建容器
    -> action-types.js          // 所以派发任务的行为标识都存与这里统一管理
    -> reducer                  // 存放每一个模块的reducer
        -> reducerA.js          // reducerA模块
        -> reducerB.js          // reducerB模块
        -> ...                  // 其他模块
        -> index.js             // 把每一个模块的reducer最后合并成为一个reducer
    -> action                   // 存放每一个模块需要进行的派发任务(ActionCreator)
        -> reducerA.js          // reducerA模块
        -> reducerB.js          // reducerB模块
        -> ...                  // 其他模块
        -> index.js             // 所有的Action进行合并

action-types.js 派发任务的标识

// reducerA模块下的任务标识
export const REDUCERA_CHANGE_N = 'REDUCERA_CHANGE_N';
export const REDUCERA_CHANGE_M = 'REDUCERA_CHANGE_M';

// reducerB模块下的任务标识
export const REDUCERB_CHANGE_N = 'REDUCERA_CHANGE_N';
export const REDUCERB_CHANGE_M = 'REDUCERA_CHANGE_M';

reducer目录

reducerA模块

import * as ActionTypes from '../action-types.js';
export default function reducerA(state={
    n: 0,
    m: 0
},action){
    switch(action.type){
        case ActionTypes.REDUCERA_CHANGE_N:
            state = {...state,n:state.n+1};
            break;
        case ActionTypes.REDUCERA_CHANGE_M:
            state = {...state,m:state.m+1};
            break;
        default:
            break;
    }
    return state;
}

reducerB模块

import * as ActionTypes from '../action-types.js';
export default function reducerB(state={
    n: 0,
    m: 0
},action){
    ...
    return state;
}

reducer-> index.js 合并reducer

/*
* 一个项目中只能有一个reducer
*
* 把每个模块单独设定的REDUCER函数最后合并成为总的REDUCER,
*   为了保证合并REDUCER过程中,每个模块管理的状态信息不会相互冲突,redux在合并的时候把容器中的状态进行分开管理(已合并REDUCER时候设置的属性名作为状态划分的属性名,把各个模块管理的状态放到自己的属性下即可)
*       此时容器中的状态
*       STATE = {
*           reducerA:{
*               n: 0,
*               m: 0
*           },
*           reducerA:{
*               n: 0,
*               m: 0
*           }
*       }
*
*       以后获取状态信息的时候,也需要把reducerA等模块名加上
*       store.getState().vote.reducerA;
*
* */
import { combineReducers } from 'redux';
import reducerA from './reducerA';
import reducerB from './reducerB';

let reducer = combineReducers({
    reducerA,
    reducerB
})

export default reducer;

store下的index.js

import { createStore } from 'redux';
import reducer from './reducer';

let store = createStore(reducer);

export default store;

Action目录

reducerA模块

/*
* 每个板块单独的ACTION-creator: 就是把DISPATCH派发时候需要传递的ACTION对象进一步统一封装处理
* */
import * as from ActionTypes from '../action-types';
let reducerA = {
    changeN(){
        return {
            type: ActionTypes.reducerA_CHANGE_N
        }
    },
    changeM(){
        return {
            type: ActionTypes.reducerA_CHANGE_M
        }
    }
}
export default reducerA;

reducerB模块

/*
* 每个板块单独的ACTION-creator: 就是把DISPATCH派发时候需要传递的ACTION对象进一步统一封装处理
* */
import * as from ActionTypes from '../action-types';
let reducerB = {
    changeN(){
        return {
            type: ActionTypes.reducerB_CHANGE_N
        }
    },
    changeM(){
        return {
            type: ActionTypes.reducerB_CHANGE_M
        }
    }
}
export default reducerB;

index.js合并

/*
* 合并所以的action-creator,类似于reducer合并,为了防止冲突,合并后的对象是以模块名称单独划分管理
*
* Action={
*   reducerA:{},
*   reducerB:{},
* }
* */

import reducerA from './reducerA'
import reducerB from './reducerB'

let action = {
    reducerA,
    reducerB
}

export default action;

利用工程化的redux渲染页面

import React,{ Component,useState } from 'react';
import ReactDOM from 'react-dom';
import * as ActionTypes from './store/action-types'
import action from './store/action/index';

import store from './store/index';


class App extends Component{
    
    changeM = ()=>{
        console.log(11);
        store.dispatch(action.vote.changeM())
    }
    
    changeN = ()=>{
        store.dispatch({
            type: ActionTypes.VOTE_CHANGE_N
        })
    }
    
    render(){
        return(
            // {/* React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。 */}
            <React.Fragment>
                <ComN store={ store }></ComN>
                <ComM store={ store }></ComM>
                <ComBtn
                    store={ store }
                    changeM={ this.changeM }
                    changeN={ this.changeN }>
                </ComBtn>
            </React.Fragment>
        )
    }
}

// 组件N
function ComN(props){
    let {store:{ getState,subscribe }} = props;
    let { n } = getState().vote;
    
    // 使用useState创建函数中的状态信息
    let [nValue,setNValue] = useState(n);
    
    subscribe(()=>{
        let { n } = getState().vote;
        setNValue(n);
    })
    
    return(
        <div>n的值: { nValue } </div>
    )
}


// 组件M
function ComM(props){
    
    let { store:{ getState,subscribe} } = props;
    let { m } = getState().vote;
    
    let [mValue,setMValue] = useState(m);
    
    subscribe(()=>{
        let { m } = getState().vote;
        setMValue(m);
    })
    
    return(
        <div>m的值: { mValue } </div>
    )
}

// 改变N,M值的组件
function ComBtn(props){
    let btn = {
        marginTop: '30px',
        display: 'inline-block',
        width: "100px",
        height: "40px",
        border: "1px solid #ccc",
        textAlign: 'center',
        lineHeight: '40px',
    }
    return(
        <div>
            <div style={ btn } onClick={ ()=>{
                props.changeN() } }>改变 n++</div>
            <div style={ btn } onClick={ ()=>{
                props.changeM() } }>改变 m++</div>
        </div>
    )
}
ReactDOM.render(<App />,document.getElementById('root'));

React-Redux

react-redux是把redux进一步封装,适配react项目,让react操作起来更简单

store文件夹中的内容和redux一模一样

在调取组件使用的时候可以优化一些操作步骤

react-redux暴露出的一些方法

Provider 根组件

当前整个项目都在Provider组件下,作用就是把创建的STORE可以提供内部任何后代组件使用(基于上下文完成的)

-> Provider组件中只允许出现一个子元素
-> 把创建的store基于属性传递给Provider(这样后代组件中都可以使用这个store了);
import React,{ Component,useState } from 'react';
import ReactDOM from 'react-dom';
import store from './store';
import { Provider } from 'react-redux';
import App from './App.js';

ReactDOM.render(<Provider>
    <App />
</Provider>,document.getElementById('root'));

connect高阶组件

/*
 * 相对于传统的REDUX,react-redux做的步骤优化
 * 1.导出的不在是我们创建的组件了,而是基于connect构造后的高阶组件
 *  export default connect(mapStateToProps,mapDispatchToProps)(自己创建的组件)
 *  react-redux做了一件非常有作用的事情
 *      以前我们需要手动基于subscribe向事件池中追加自己的方法,已达到容器信息改变,执行我们追加的方法,重新渲染组件的目的.
 *      react-redux帮我们做了这件事: 所有用到Redux容器状态信息的组件,都会向事件池追加一个方法,当状态信息改变,通知方法执行,吧最新的状态下信息作为属性传递给组件,组件属性改变,组件也会重新渲染.
*/
import React,{ Component } from 'react';
import { connect } from 'react-redux';
import action from './store/action/index';
class App extends Component{
    render(){
        return(
            <React.Fragment>
                <ComN}></ComN>
                <ComM></ComM>
                <ComBtn
                    changeM={ ()=>{ this.props.changeM() }}
                    changeN={ ()=>{ this.props.changeN() }}>
                </ComBtn>
            </React.Fragment>
        )
    }
}


// 组件M
function ComM(props){
    return(
        <div>m的值: { props.m } </div>
    )
}

// 组件m
function ComN(props){
    return(
        <div>m的值: { props.n } </div>
    )
}

/*
// 把redux容器中的状态信息遍历,赋值给当前组件的属性(state);
let mapStateToProps = (state) =>{
    // => state: 就是redux容器中的状态信息
    // 我们返回的是啥就把他挂载到当前组件的属性上(redux中存储很多信息,我们想用啥返回啥即可)
    return {
        ...state.vote
    }
}
// 把redux中的dispatch派发行为,也赋值给组件的属性(actionCreator)
let mapDispatchToProps = (dispatch)=>{
    // => dispatch: Store中存储的DISPATCH方法
    // => 返回的是啥就相当于吧啥挂载到组件的属性上(一般我们挂载一些方法,这些方法中我们完成了dispatch派发热舞操作)
    return {
        changeM(){
            dispatch(action.changeM())
        },
        changeN(){
            dispatch(action.changeN())
        }
    }
}
export default connect(mapStateToProps,mapDispatchToProps)(App);
*/
export default connect((state)=>{
    return{
        ...state.vote
    }
,action.vote)(App);// react-redux帮我们做了一件事情,把action-creator中编写的方法(返回ACTION对象的方法),自动狗线程dispatch派发热舞的方法,也就是mapDispatchToProps

Redux中间件

redux-logger

redux-thunk

redux-promise

redux-saga

总结:

都在这里


细节之处,略有疏漏,如有不足或者错误的地方,欢迎大佬批评并加以指正.谢谢.