美文网首页
React 学习笔记 05 Redux状态管理

React 学习笔记 05 Redux状态管理

作者: Upcccz | 来源:发表于2019-04-13 21:41 被阅读0次

Redux

安装

npm i redux -S

类似于vuex的状态托管插件库

使用
import { createStore } from 'redux'

// 1.定义reducer 相当于mutation 
// 必须是同步的 因为必须要有return 一个新的state 不是修改state

// 数组新增
// return [
//     ...state,
//     action.payload
// ]

// 数组删除
// return [
//     ...state.slice(0,action.index),
//     ...state.slice(action.index+1)
// ]

// 切换某一项
// return [
//     ...state.slice(0,action.index),
//     {
//         ...state[action.index],
//         isFlag:!...state[action.index].isFlag,
//     }
//     ...state.slice(action.index+1)
// ]



function reducer(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state; // 这一行必不可少 因为初始化时,switch语句就执行的是default 返回初始值
  }
}

// 2.创建容器 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(reducer);

// 3.使用状态
class App extends React.Component {
    render(){
        return (
            <div>
                获取容器中的值:{store.getState()}
                <button onClick={this.add.bind(this)}>增加</button> 
                <button onClick={this.sub}>减少</button>
                {/* 如果函数的调用没有使用当前组件中的数据可以不bind*/}
            </div>
        )
    }

    add(){
        store.dispatch({type:'INCREMENT'})
        // 此时不会立即刷新视图

        // 1.如果想刷新可以直接调用forceUpdate
        // this.forceUpdate()

        // 也可以使用下面的subscribe 监听状态
    }

    sub(){
        store.dispatch({type:'DECREMENT'})
    }
}

ReactDOM.render(<App/>.document.getElementById('app'))

// 状态发生变化 就会触发
store.subscribe(() =>
  console.log(store.getState())
  // 统一更新  this.froceUpdate()

  // 或者重新调用ReactDOM.render() render重新渲染就会更新视图

  ReactDOM.render(<App/>.document.getElementById('app'))
);

// store.subscribe 会返回一个函数 调用一下这个函数就可以移除监听
// var unSub = store.subscribe(() =>{}); unSub()
规范 设计Action Creator
import { createStore } from 'redux'

function reducer(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + action.num;
  case 'DECREMENT':
    return state - action.num;
  default:
    return state; 
  }
}

// 抽离action对象 别的组件也可以方便调用 方便灵活传参

// 设计Action Creator
function addCount(num){
    return {
        type:'INCREMENT',
        num
    }
}
// 简写
// const addCount = (num)=>{type:'INCREMENT',num}

function subCount(num){
    return {
        type:'DECREMENT',
        num
    }
}

let store = createStore(reducer);

// 3.使用状态
class App extends React.Component {
    constructor(props){
        super(props)
        this.state = {
            num:1
        }
    }

    render(){
        return (
            <div>
                获取容器中的值:{store.getState()}
                <input type="text" value={this.state.num} onChange={(e)=>this.setState({num:+e.target.value})}/>
                <button onClick={this.add.bind(this)}>增加</button> 
                <button onClick={this.sub.bind(this)}>减少</button>
            </div>
        )
    }

    add(){
        store.dispatch(addCount(this.state.num))
    }
    sub(){
        store.dispatch(subCount(this.state.num))
    }
}

ReactDOM.render(<App/>.document.getElementById('app'))

// 状态发生变化 就会触发
store.subscribe(() =>
  ReactDOM.render(<App/>.document.getElementById('app'))
);
多个状态 合并reducer

import  { createStore,combineReducers } from 'redux'

// 1.定义count的reducer  相当于 vuex 中的{state:{count:0}}
function count(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
        return state + action.num;
    case 'DECREMENT':
        return state - action.num;
    default:
        return state;
  }
}

// 2.定义flag的reducer  相当于 vuex 中的{state:{count:0,flag:false}}
function flag(state = false, action) {
  switch (action.type) {
    case 'toggle':
        return !state
    default:
        return state; 
  }
}

// 3.使用combine合并 键值一致可以省写
// 实际传值 --> {'count':count,'flag':flag}
const reducers = combineReducers({
    count,flag
})

let store = createStore(reducers);

// 一般在reducer里面设置默认值
// 多个的时候也可以在这里设置默认值,不建议使用 
// let store = createStore(reducers,{
//     count:0,
//     flag:false
// });

class App extends React.Component {
    render(){
        return (
            <div>
                test {store.getState().count}
                <hr/>
                <p> {store.getState().flag} </p>
            </div>
        )
    }
}

ReactDOM.render(<App/>.document.getElementById('app'))

// 状态发生变化 就会触发
store.subscribe(() =>
  ReactDOM.render(<App/>.document.getElementById('app'))
);
使用react-redux来优化刷新视图

安装:npm i react-redux -S

是连接React和Redux的桥梁,所以要先引入这两个,在引入react-redux

API

+ Provider: 用于挂载store对象的根组件
+ connect函数:将store中的状态和方法给所有组件去访问。是一个高阶组件的思想,共享数据加强组件。

使用

import React from 'react'
import ReactDOM from 'react-dom'
import  { createStore,combineReducers } from 'redux'

import {Provider,Connect} from 'react-redux'

function count(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
        return state + action.num;
    case 'DECREMENT':
        return state - action.num;
    default:
        return state;
  }
}

const reducers = combineReducers({
    count
})

let store = createStore(reducers);

// 组件要取store中的数据 或者修改其中的数据 都需要拆分组件
// 拆分为UI组件和容器组件,因为要通过Connect来加强组件 才能共享状态
// UI组件负责视图,而容器组件就是通过Connect加强后的组件

class UIComponent extends React.Component {
    render(){
        return (
           <div>
                使用共享的状态:{this.props.count}
                <hr/>
                使用App传来的数据:{this.props.appData}
                <hr/>
                <button onClick={this.props.add}>增加</button>
                <button onClick={this.props.show}>show</button>
           </div>
        )
    }
}
// 这个函数作用:给UIComponent共享哪些状态 
// 将 store中的state 映射为UIComponent中的props
// 如上 在UIComponent中使用props调用
function mapStateToProps(state,parentProps){
    // 第一个参数就是state == store.getState
    // 第二个参数就是StoreComponent的props对象 用于App传值给UIComponent
    return {   
        count:state.count,
        appData:parentProps.appData
    }
}

// 这个函数作用:给UIComponent共享哪些操作状态的函数
// 将store中的dispatch映射为UIComponent中的props
function mapDispatchToProps(dispatch,parentProps){
    // 第一个参数就是dispatch == store.dispatch
    // 第二个参数也一样
    return {
        add = ()=>dispatch('INCREMENT'),
        show:parentProps.show
    }
    // 调用this.props.add 就会分发dispatch('INCREMENT')
}

// Connect 接受2个参数 都需要是函数,返回一个函数,这个函数去加强UIComponent
const StoreComponent = Connect(mapStateToProps,mapDispatchToProps)(UIComponent)

// StoreComponent就是加强后的容器组件挂载后,共享状态的时候就会自动更新了,
// 而不在需要监听store.subcribe

class App extends React.Component {
    render(){
        return (
            <Provider store={store}>
                test {store.getState().count}
                <hr/>
                <StoreComponent appData="我是app" show={this.show} />
            </Provider>
        )
    }

    show = ()=>{
        console.log('我是app中的show方法')
    }
}

ReactDOM.render(<App/>.document.getElementById('app'))

中间件

如果状态的修改是异步操作,reducer只能做同步的(同mutation),就需要定义一个函数,去完成异步操作然后去dispatch,去触发reducer改变更新数据。

问题在于这个函数怎么才能去调用store.dispatch? vuex里面 actions里面直接能访问store 而react呢

这一个异步操作就需要一个中间件来完成,类似于vuex里面的actions。

vuex:组件 store.dispatch actions里面的函数 => actions里面的异步操作函数 commit mutations里面的函数 => 改变更新数据

redux:组件 store.dispatch => 中间件拦截住 然后完成异步操作之后 在dispatch 触发reducer => 改变更新数据

import { applyMiddleware, createStore } from 'redux'


function count(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state; // 这一行必不可少 因为初始化时,switch语句就执行的是default 返回初始值
  }
}

function fn1(){

}

function fn2(){
    
}

let store = createStore(count,100,applyMiddleware(fn1,fn2));
// 第一个参数:reducer
// 第二个参数可选:reducer的默认值
// 第三个参数可选:一个回调函数,参数是createStore函数本身,用于处理中间件返回的值

class App extends React.Component {
    render(){
        return (
            <div>
                获取容器中的值:{store.getState()}
                <button onClick={this.add}>增加</button> 
                <button onClick={this.sub}>减少</button>
            </div>
        )
    }

    add(){
        store.dispatch({type:'INCREMENT'})
    }

    sub(){
        store.dispatch({type:'DECREMENT'})
    }
}

ReactDOM.render(<App/>.document.getElementById('app'))

// 状态发生变化 就会触发
store.subscribe(() =>
  ReactDOM.render(<App/>.document.getElementById('app'))
);
理解源码
function fn1(){

}

function fn2(){
    
}

let store = createStore(count,100,Redux.applyMiddleware(fn1,fn2));

Redux.applyMiddleware(fn1,fn2) 就会返回一个这样的函数 function(createStore){}

这个函数(function(createStore){} )肯定是可以访问到fn1 和 fn2
主要的是 这个函数再什么情况下被调用 fn1 和 fn2 又能做什么?

function(createStore){} 这个函数参数是createStore,就可以在这个函数创建一个初始store,然后经过一些处理,返回新的store容器

function(createStore){
  /* 返回一个函数签名跟 createStore 一模一样的函数,亦即返回的是一个增强版的 createStore */
    return function(reducer,preloadedState,enhancer){

        // 用原 createStore 先生成一个 store,其包含 getState / dispatch / subscribe / replaceReducer 四个 API
      var store = createStore(reducer, preloadedState, enhancer)
      
      var dispatch = store.dispatch // 指向原 dispatch
      var chain = [] // 存储中间件的数组
  
      // 提供给中间件的 API(其实都是 store 的 API)
      var middlewareAPI = {
        getState: store.getState,
        dispatch: (action) => dispatch(action)
      }
      
      // 给中间件“装上” API,即fn1 和 fn2 就可以访问到getState 和 dispatch 了
      // fn1 fn2 就是 ...middlewares 去接受的 
      chain = middlewares.map(middleware => middleware(middlewareAPI))

      // chain = [fn1(store),fn2(store)] 这里的store是简易版的
      
      // 串联所有中间件 所以 chain存储的是 fn1 和 fn2 这两个中间件的返回值
      // (这两个中间件也一定返回的是一个函数)
      // 然后会把dispatch先传给fn2中间调用 
      // 然后将调用后返回的值 在交给fn1中间件调用 
      dispatch = compose(...chain)(store.dispatch)
      // compose(f, g, h)(action) => f(g(h(action)))
      // 例如,chain 为 [M1, M2],而 compose 是从右到左进行“包裹”的
      // 最后,我们得到串联后的中间件链:(M1(M2(store.dispatch)))
      // 返回一个最新的dispatch函数
  
      // 返回新的容器对象
      return {
        ...store, // store 的 API 中保留 getState / subsribe / replaceReducer
        dispatch  // 新 dispatch 覆盖原 dispatch,往后调用 dispatch 就会触发 chain 内的中间件链式串联执行
      }
    }
   
} 

所以真正的fn1 和 fn2 就应该这么来用

function fn2(store){
    // 返回一个函数 会在上面compose的时候被调用 且会传进来一个dispatch
    return (dispatch)=>{
        // dispatch 返回的函数(第三层函数) 才是真正store.dispatch需要调用的函数

        // 组件中 store.dipatch(addCount(num)) 
        // addCount(num) == 就是上面说的 Action Creator {type:xxx,payload:xxx}  == 也就是这里的action

        return (action)=>{
            return dispatch(action) // 将{type:xxx,payload:xxx} 最终交给 dispatch去分发 
            // dispatch(action)  就会触发 fn1 中第三层(最里层函数)函数的调用

        }
        
    }
}

function fn1(){
    return (dispatch)=>{
        return (action)=>{ // 这个函数会被上面fn2最后的dispatch(action)触发
            // 如果fn1之前还有中间件 就 return dispatch(action) 没有就直接触发 dispatch(action)
            dispatch(action) 
            // fn1 左边没有中间件了 那么这个dispatch就是最终的的dispatch 就会触发reducer 然后改变更新数据
            // 上面那个不会触发reducer  因为它之前(左边)还有中间件

            // 这样 我们就可以在这个 dispatch 和 render数据更新 之间做我们想做的事 
            // 所以可以做一个 logger 如下
        }

        // return (action)=>{
        //     console.log(action.type + '触发前',store.getState())
        //     dispatch(action)
        //     console.log(action.type + '触发后',store.getState())
        // }
    }
}

let store = createStore(count,100,Redux.applyMiddleware(fn1,fn2));

异步请求 更新状态
import { applyMiddleware, createStore } from 'redux'

function count(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - action.num;
  default:
    return state;
  }
}

const myThunk = (store) => (dispatch) => (action)=>{
    typeof action == 'function' ? action(dispatch) : dispatch(action)
}


let store = createStore(count,100,applyMiddleware(myThunk));

class App extends React.Component {
    render(){
        return (
            <div>
                获取容器中的值:{store.getState()}
                <button onClick={this.add}>增加</button> 
                <button onClick={this.sub}>模拟异步减num</button>
            </div>
        )
    }

    add(){
        store.dispatch({type:'INCREMENT'})
    }

    sub(){
        store.dispatch(this.asyncGetData(1)) 
    }

    asyncGetData(num){ 
        return function(dispatch){
            setTimeout(()=>{
                dispatch({
                    type:'DECREMENT',
                    num
                })
            },100)
        } 
        // 分发一个函数 之前都是分发一个对象

        // return的这个函数 被store.dispatch调用了 就会被myThunk的第三层拦截
        // 通过验证 这次的action是一个函数 就会把dispatch 传给这个函数action并调用了== action(dispatch)
        // 就会执行这个 setTimeout, 这个setTimeout就会在1000毫秒之后,再去dispatch 这个对象action
        // 然后又会被myThunk 拦截,这次验证发现是一个对象 ,就会dispatch这个action去触发reducer的更新

        // 如果 myThunk之间还有 中间件, 那么 最后验证是一个对象的时候就应该是return dispatch(action) 而不是直接调用了
    }
}

ReactDOM.render(<App/>.document.getElementById('app'))

// 状态发生变化 就会触发
store.subscribe(() =>
  ReactDOM.render(<App/>.document.getElementById('app'))
);

相关文章

  • React 学习笔记 05 Redux状态管理

    Redux 安装 npm i redux -S 类似于vuex的状态托管插件库 使用 规范 设计Action Cr...

  • react-redux

    redux 全局状态管理 react-redux 优化模块 优化redux的使用过程 通过react-redux ...

  • Flutter redux 使用与理解

    Flutter 状态管理redux 方案理解与实践 redux 数据管理方式来自 react ,React 的数据...

  • React实战之Redux

    Redux、React-Redux 简介 Redux 是 JavaScript 状态容器,提供可预测化的状态管理。...

  • redux

    Redux Redux 是JavaScript状态的容器,提供可预测化的状态管理 Redux不是React必须的,...

  • react全家桶

    react全家桶有:react + redux(状态管理) +react-router(路由) + axios +...

  • React中的Redux

    学习必备要点: 首先弄明白,Redux在使用React开发应用时,起到什么作用——状态集中管理 弄清楚Redux是...

  • react-redux 源码解析

    前提 了解 redux,查看 Redux 分析 react-redux 解决的问题 负责应用的状态管理,保证单向数...

  • Redux

    Redux 专注于状态管理的库 Redux专注于状态管理和react解耦 单一状态,单向数据流 核心概念:stor...

  • redux

    状态管理,react没有提供一个整个应用的状态管理,所以就有redux的出现,首先声明 redux 非必须。red...

网友评论

      本文标题:React 学习笔记 05 Redux状态管理

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