redux

Flux

Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC架构是同一类东西,但是更加简单和 清晰。Flux存在多种实现(至少15种)

https://github.com/voronianski/flux-comparison

Facebook Flux是用来构建客户端Web应用的应用架构。它利用单向数据流的方式来组合React 中的视图组件。它更像一个模式而不是一个正式的框架,开发者不需要太多的新代码就可以快速的上手Flux。

image-20210602101025512

redux(重点)

redux介绍及设计

  • redux 是flux架构模式中15种实现中的一种。可以跟vue react,jq …一起用,只是更习惯跟react 一起用

  • redux共享状态管理,把所有需要共享的状态统一放到redux中管理,组件里面不再放状态,通过redux共享状态管理。实现组件之间直接的通信(类似vuex)。状态一旦被共享,需要严格遵循工作流程,可以通过redux-devtools调式工具查bug

  • Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树 (state对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers),这样就可以进行数据追踪,实现时光旅行。

redux使用三大原则

  • state 以单一对象存储在store 对象中
  • state只读(每次都返回一个新的对象)
  • 使用纯函数reducer 执行 state 更新

redux 工作流

image-20210605090310041
  • 面试重点:请说一下flux ,redux ,react-redux怎么理解的?

    flux:是一种架构思想,专门解决软件结构问题。

    redux是flux架构模式中15种实现中的一种。实现状态的共享,组件之间的通信。

    react-redux 是对redux的进一步封装,专门提供给react使用

  • Redux的运作流程:

在组件上,调用store.dispatch(action)方法,并携带action数据。
然后store自动调用reducer函数,store传递两个参数给reducer函数:当前state和收到的action。其中,reducer函数必须是一个纯函数,该函数会返回一个新的state。
根reducer会把多个子reducer的返回结果合并成最终的应用状态,在这一过程中,可以使用Redux提供的combineReducers方法。使用combineReducers方法时,action会传递给每个子的reducer进行处理,在子reducer处理后会将结果返回给根reducer合并成最终的应用状态。
store调用store.subscribe监听state的变化,state一旦发生改变就会触发store的更新,最终view会根据store数据的更新刷新界面。

  1. 页面调用diapatch方法,携带action数据。
  2. reducer监听到转发过来的action,处理请求,修改存放在store里的状态state。
  3. 页面订阅store里的状态,一旦状态发生改变,就会触发。得到第二步修改后最新的状态值

redux使用

什么情况下使用redux

  1. 总体原则:能不用就不用
  2. 某个组件的状态需要共享
  3. 某个状态需要在任何地方都可以拿到
  4. 一个组件需要改变全局状态
  5. 一个组件需要改变另一个组件的状态

下载依赖包

npm install --save redux
  1. 新建redux|store.js 文件。导入redux ,创建store对象 并导出
import {createStore} from 'redux'

const reducer = ()=>{
  // 修改状态只能在reducer中进行修改
}

const store = createStore() // 控制共享状态
// 通过store.subscribe和 store.dispatch (store 的原理就是订阅发布模式)

export default store
  1. reducer 修改状态的地方

更改状态通过reducer,react 要求不能直接改状态,改之前需要深复制一份

const reducer = (prevState={ // es6默认值,可以管理共享状态
    // 初始状态
    isCollapsed:false, //是否折叠
    list:[], //共享数组
    userInfo:{}, //共享用户登录信息
})=>{
  // reducer相当于 vue 的mutations
  // 修改状态只能在reducer中进行修改。保留老状态返回新状态 
  // 1.传入prevState 老的状态 (接收状态)
  // 2. 深复制一个新的状态(返回状态)
    let newState = {...prevState}
    return prevState
}

redux的核心API

createStore()

作用:创建包含指定reducer的对象

import {createStore} from 'redux'
import counter from './reducers/counter'
const store = createStore(counter)
store对象:

作用:redux库最核心的管理对象。 它的内部维护着:state,reducer
核心方法:getState() 、dispatch(action)、 subscribe(listener)

store.getState()
store.dispatch({type:'INCREMENT', number})
store.subscribe(render)
applyMiddleware()

作用:应用上基于redux的中间件(插件库)

import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'  // redux异步中间件
const store = createStore(
  counter,
  applyMiddleware(thunk) // 应用上异步中间件
)
combineReducers()

作用:合并多个reducer函数

export default combineReducers({
  user,
  chatUser,
  chat
})

redux的三个核心概念

action

标识要执行行为的对象;包含2个属性:
type: 标识属性,值为字符串,必要属性
xxx:数据属性,值类型任意,可选属性

const action = { type: 'INCREMENT', data: 2 }
/** Action Creator(创建Action的工厂函数) **/
const increment = (number) => ({type:INCREMENT, data: number})
reducer:

根据旧的state和action, 产生新的state的纯函数

export default function counter(state=0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + action.data
        case 'DECREMENT':
            return state - action.data
        default:
            return state
    }
}

注意:返回一个新的状态,不要修改原来的状态

store:

将state,action与reducer联系在一起的对象:

import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
/** 
此对象的功能:
getState(): 得到state
dispatch(action): 分发action, 触发reducer调用, 产生新的state
subscribe(listener): 注册监听, 当产生了新的state时, 自动调用 
**/ 

news后台系统项目案例:

显示隐藏侧边栏
  • redux|store.js (状态管理的文件 )
import { createStore } from 'redux'

const reducer = (prevState = { // prevState 每次再执行的时候会传入上一个状态
  isCollapsed: false, //是否折叠
  list: [], //共享数组
  userInfo: {}, //共享用户登录信息
}, action) => {
  // 修改状态只能在reducer中进行修改。保留老状态返回新状态
  let newState = { ...prevState } // 深复制,后期只能改newState
  /*===== 改变状态 =====*/
  let { type } = action
  switch (type) { // type从dispatch 中传入
    case "change_collapsed":
      newState.isCollapsed = !newState.isCollapsed
      return newState
    case "change_list":
      return newState
    case "change_userinfo":
      return newState
    default:
      return prevState // 默认没改的 return 老状态
  }
  /*======================*/
}

const store = createStore(reducer)

export default store
  • 被修改的组件内写订阅模式 store.subscribe(()=>{})

    SideBar.js

const [selfCollapsed, setselfCollapsed] = useState(store.getState().isCollapsed)

useEffect(() => {
    store.subscribe(() => {
       // store.getState() 获取到最新的状态
       setselfCollapsed(store.getState().isCollapsed)
    })
}, [])

<Sider trigger={null} collapsible collapsed={selfCollapsed} >
    {/*  不能直接 collapsed={store.getState().isCollapsed} */}
    <div> 侧边栏菜单组件 </div>
</Sider>

退出登录后再登录会报以下警告:

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function

注: react 切换路由会导致组件被销毁 ,组件内的状态和方法也会被销毁。而 useEffect 中的订阅是一个回调函数,函数不会被主动销毁,所以再登录的时候,退出登录后再重新登录,会出现重复订阅,走了两次回调。解决办法:需要取消订阅 retrun()=>{}

useEffect(() => {
    var cancel = store.subscribe(()=>{
        //  订阅完后会返回一个取消订阅的方法
        setselfCollapsed(store.getState().CollapsedReducer.isCollapsed)
    })
    return ()=>{//取消订阅
        cancel()     
    }
}, [])
  • 需要改变状态的组件内写入发布模式 store.dispatch({type:"aaa", payload:"1111"})

如果需要传参 直接在dispatch对象中 通过key/value 值传到store 中

TopHeader.js

// 发布方法中必须传东西。类型会传入到store 中的action
const [isCollapsed, setisCollapsed] = useState(true)
const toggle = (isCollapsed) => {
    setisCollapsed(!isCollapsed) 
    // 点击取反,状态被改变 ,把状态dispatch 到store 
    store.dispatch({ // 必须传入一个type 属性
        type: "change_collapsed",
    })
}

ActionCreator

// 生产一个函数,谁调用谁就有这个函数的东西
function CollapsedAction () {
  return {
    type: "change_collapsed"
  }
}

export default CollapsedAction
 store.dispatch(CollapsedAction())

纯函数

必须满足以下两个原则:

  1. 同样的输入得到同样的输出。
  2. 对外界没有副作用

纯函数在接收函数的时候,同样的参数返回同样的结果,并保证外面没有任何副作用

//================ 不是纯函数 ==================
var myname= "kerwin"
function test(myname){
    myname="xiaoming"
    return myname
}
test() // myname全局作用域 函数内对变量重新赋值。变量会受到影响 

//==================纯函数==========================
var myname= "kerwin"
function test(myname){
    myname="xiaoming"
    return myname
}
test(myname) // 不会影响,myname被当成实参传进函数,函数内是对形参进行了改变,不会影响外面的变量 

//================= 不是纯函数 ========================
var prevState = { myname:"kerwin" }
function test(prevState){
    prevState.myname="xiaoming"
    return prevState
}
test(prevState) // 会影响 复杂类型引用的是一个地址,里面改变会影响外面(浅拷贝),所以需要再深复制一份。就是纯函数设计

//================= 纯函数 ==========================
var prevState = { myname:"kerwin" }
function test(prevState){
    var newState = {...prevState}
    newState.myname="xiaoming"
    return newState
}
test(prevState) 

总结:reducer保持纯函数设计

reducer 拆分

如果不同的action所处理的属性之间没有联系,我们可以把 Reducer 函数拆分。 不同的函数负责处理不同属性,最终把它们合并成一个大的 Reducer 即可。

多人合作开发项目的时候需要 拆分 Reducer 成多个文件 ,保证不会改同一个文件, 自己管理自己的reducer ,最终再 store 中通过combineReduces()合并成一个大的 Reducer。

例:把控制折叠的状态拆分出来成一个单独的 Reducer

const CollapsedReducers = (prevState = {
  isCollapsed: false, //是否折叠
}, action) => {
  let newState = { ...prevState } // 深复制,后期只能改newState
  // 改变状态
  let { type } = action
  switch (type) { // type从dispatch 中传入
    case "change_collapsed":
      newState.isCollapsed = !newState.isCollapsed
      return newState
    default:
      return prevState // 默认没改的 return 老状态
  }
}

export default CollapsedReducers

store.js 中把拆分出的 Reducer 函数进行合并

import { createStore, combineReducers } from 'redux'
import RightListReducer from './reducers/RightListReducer'
import CollapsedReducers from './reducers/CollapsedReducers'
// Reducer 函数拆分,最终通过combineReduces()合并成一个大的 Reducer 即可。

const reducer = combineReducers({
  RightListReducer,
  CollapsedReducers
})
const store = createStore(reducer)
export default store

注:合并的时候combineReduces 会把每一个拆分的reducer 的进行遍历,订阅的时候需要多点一层到里面的对象

侧边栏中订阅需要多点一层

store.subscribe(() => {
    setselfCollapsed(store.getState().CollapsedReducers.isCollapsed)
})

总结:

redux 是一个专门的状态管理库(在vue等当中可以使用 但是在react中会比较多)。集中的管理react中多个组件的状态

需求场景:
某个组件的状态需要共享的时候
一个组件需要改变另外一个组件状态的时候
组件中的状态需要在任何地方都可以拿到

三大原则:
1.单一数据源 整个react中的状态都会被统一的管理到store
2.state是只读的 我们不能直接改变state 而是要通过触发redux中的特定方法来进行修改
3.使用纯函数来执行修改操作:action来改变redux中的state

  • 面试:了解 redux 么,说一下 redux 吧

  • redux 是一个应用数据流框架,主要是解决了组件间状态共享的问题,原理是集中式管理,主要有三个核心方法,action,store,reducer,工作流程是view 调用 store 的 dispatch 接收 action 传入 store,reducer 进行 state 操作,view 通过 store 提供的 getState 获取最新的数据,flux 也是用来进行数据操作的,有四个组成部分 action,dispatch,view,store,工作流程是view 发出一个 action,派发器接收 action,让 store 进行数据更新,更新完成以后 store 发出 change,view 接受 change 更新视图。Redux 和 Flux 很像。主要区别在于 Flux 有多个可以改变应用状态的 store,在 Flux 中dispatcher 被用来传递数据到注册的回调事件,但是在 redux 中只能定义一个可更新状态的 store,redux 把 store 和 Dispatcher 合并,结构更加简单清晰

  • 新增 state,对状态的管理更加明确,通过 redux,流程更加规范了,减少手动编码量,提高了编码效率,同时缺点时当数据更新时有时候组件不需要,但是也要重新绘制,有些影响效率。一般情况下,我们在构建多交互,多数据流的复杂项目应用时才会使用它们

  • redux 有什么缺点

一个组件所需要的数据,必须由父组件传过来,而不能像 flux 中直接从store 取。

当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate 进行判断。

redux中间件

在redux里,action仅仅是携带了数据的普通js对象。action creator返回的值是这个 action类型的对象。然后通过store.dispatch()进行分发。同步的情况下一切都很完美,但是reducer 无法处理异 步的情况。 那么我们就需要在action和reducer中间架起一座桥梁来处理异步。这就是 middleware。

redux 的 异步状态

reducer 不能走异步 。需要 通过redux 中间件

News后台系统项目案例:

例:某个页面(RightList) 通过异步ajax 取回来的数据(不常更新的数据),后期刷新的时候不需要重复请求,而是走缓存 。

useEffect(()=>{
    if(store.getState().RightListReducer.list.length===0){ // 数组长度为0进行 ajax第一次请求     
    }else{ // 不等于0,说明已经有数据,直接走缓存
        
    }
})

RightList.js

const [dataSource, setdataSource] = useState([])
// 异步请求数据 
useEffect(() => {
    if (store.getState().RightListReducer.list.length === 0) { // 数组长度为0进行 ajax第一次请求     
        store.dispatch(RightListAction()) // RightListAction()立即执行
    } else { // 不等于0,说明已经有数据,直接走缓存
    }
}, [])

RightListAction.js

import http from "../../util/http"

function RightListAction () {
  // 错误写法:
  /* http.get("/rights?_embed=children").then(res => {
    console.log(res.data);
    return {
      type: "change_list",
      payload:res.data
    }
  }) */

  // 正确写法:
  return http.get("/rights?_embed=children").then(res => {
      var list = res.data.map(item => {
      if (item.children.length === 0) {
        delete item.children
        return item
      }
      return item
    })
    return {
      type: "change_list",
      payload: res.data
    }
  })
  // 在异步前面加return 返回出去的是一个promise对象,但会报错:Actions must be plain objects
}

export default RightListAction

http.get() 是异步请求ajax ,而RightListAction()是立即调用,不会等异步回来的数据,所以在异步里面return会返回一个undefined。在http.get异步外面return,数据还没取回来就return。会造成页面没有数据,因为已经渲染完了,才异步取回的数据,已经晚了。

Actions must be plain objects. Instead, the actual type was: 'Promise'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples

解决:需要引用中间件

方案一:redux-promise( 需要npm 下载 )

在store.js 中

import { createStore, combineReducers ,applyMiddleware} from 'redux'
import RightListReducer from './reducers/RightListReducer'
import reduxPromise from 'redux-promise'

const reducer = combineReducers({
  RightListReducer
})
// applyMiddleware(reduxPromise) 提供一个reduxPromise中间件
const store = createStore(reducer,applyMiddleware(reduxPromise))

export default store

默认 store.dispatch 只支持 普通对象 { type:“change_collapge”}

应用reduxPromise 中间件之后, store.dispatch 支持 promise对象

页面中请求数据RightList.js

useEffect(() => {
    if (store.getState().RightListReducer.list.length === 0) { // 数组长度为0进行 ajax第一次请求     
        store.dispatch(RightListAction()) // RightListAction()立即执行
    } else { // 不等于0,说明已经有数据,直接走缓存
        setdataSource(store.getState().RightListReducer.list)
    }

    const cancel = store.subscribe(() => {
        setdataSource(store.getState().RightListReducer.list)
    })
    return () => {
        cancel() // 取消订阅
    }
}, [])
function RightListAction () {
  return http.get("/rights?_embed=children").then(res => {
      var list = res.data.map(item => {
      if (item.children.length === 0) {
        delete item.children
        return item
      }
      return item
    })
    return {
      type: "change_list",
      payload: res.data
    }
  })
}

请求回数据后对状态进行赋值

const RightListReducer = (prevState = {
  list: [], //共享数组
}, action) => {
  let newState = { ...prevState } // 深复制,后期只能改newState
  // 改变状态
  let { type, payload } = action
  switch (type) { // type从dispatch 中传入
    case "change_list":
      newState.list = payload
      return newState
    default:
      return prevState // 默认没改的 return 老状态
  }
}

export default RightListReducer
方案二:thunkPromise

下载:

npm i --save redux-thunk
import {createStore,combineReducers,applyMiddleware} from 'redux'
import RightListReducer from './reducers/RightListReducer'
import reduxThunk from 'redux-thunk'
const reducer = combineReducers({
    RightListReducer
})

const store = createStore(reducer,applyMiddleware(reduxThunk))
// 应用thunkPromise 中间件之后, `store.dispatch` 支持 function对象
export default store

在RightsList.js组件中 store.dispatch()

useEffect(() => {
    if (store.getState().RightListReducer.list.length === 0) { // 数组长度为0进行 ajax第一次请求     
        store.dispatch(RightListAction()) // RightListAction()立即执行
    } else { // 不等于0,说明已经有数据,直接走缓存
        setdataSource(store.getState().RightListReducer.list)
    }

    const cancel = store.subscribe(() => {
        setdataSource(store.getState().RightListReducer.list)
    })
    return () => {
        cancel() // 取消订阅
    }
}, [])
function RightListAction () { 
    return (dispatch)=>{
        http.get("/rights?_embed=children").then(res => {
            var list = res.data.map(item => {
                if (item.children.length === 0) {
                    delete item.children
                    return item
                }
                return item
            })
            dispatch({
                type:"change_list",
                payload:list
            })
        })
    }
}

请求回数据后对状态进行赋值

const RightListReducer = (prevState = {
  list: [], //共享数组
}, action) => {
  let newState = { ...prevState } // 深复制,后期只能改newState
  // 改变状态
  let { type, payload } = action
  switch (type) { // type从dispatch 中传入
    case "change_list":
      newState.list = payload
      return newState
    default:
      return prevState // 默认没改的 return 老状态
  }
}

export default RightListReducer

i. 中间件的由来与原理、机制

export default function thunkMiddleware({ dispatch, getState }) { 
    return next => action => 
    typeof action === 'function' ? 
        action(dispatch, getState) : 
    next(action); 
}

react-redux

react-redux:一个插件库, 基于redux 的再次封装,专门给react 用的

react-redux 提供一个新的组件 connect (包装组件),在自己的组件在外面再包一层,由connect 来完成订阅发布 。 在最外层在包一个provide组件,

将所有组件分为两大类:
UI组件:

  • 只负责UI的呈现,不带有任何业务逻辑

  • 通过props接受数据(一般数据和函数)

  • 不使用任何redux的API

  • 一般保存在components文件夹中

  • List item

容器组件:

负责管理数据和业务逻辑,不负责UI呈现

  • 使用redux的API
  • 一般保存在containers文件夹下

下载

npm i --save react-redux

Provider:

让所有组件都可以得到state数据

在根组件(App.js) 最外层包一个供应商组件 Provider,并传入store 属性

import './App.css';
import NewsRouter from './router';
import { Provider } from 'react-redux'
import store from './redux/store';
function App () {
  return (
    <Provider className="App" store={store}>
      <NewsRouter></NewsRouter>
    </Provider>
  );
}

export default App;

connect():

用于包装UI组件生成容器组件(将组件与react-redux连接起来)

显示隐藏侧边栏: 主要是通过控制 isCollapsed

react-redux 写法 在SideBar外面套一个connect(父组件),通过connect 来订阅

import { connect } from 'react-redux' 

function SideBar (props) {
    .....
 <Sider trigger={null} collapsible collapsed={props.isCollapsed} >
  {/* 
    侧边栏组件。。。。显示隐藏。。
    collapsed={props.isCollapsed} 
    connect已经把状态映射给Sidebar Sidebar里面要用只需通过props.isCollapsed 
   */}
 </Sider>
}

const mapStateToProps = (state) => { // 映射状态到属性(state  store中的状态)
  return {
    isCollapsed: state.CollapsedReducers.isCollapsed
  }
}
// 此时 SideBar 的父组件是 connect,。。。。
export default connect(mapStateToProps)(SideBar)

TopHeader 图标的切换

// 从redux action中导入 CollapsedAction
import CollapsedAction from '../../redux/ActionCreator/CollapsedAction'; 

function SideBar (props) {
    ....
 const toggle = (isCollapsed) => {
    setisCollapsed(!isCollapsed)
    props.CollapsedAction()
  }
    .....
}

// react-redux 写法: 
const mapStateToProps = (state) => { // 映射状态到属性  state  store中的状态
  return {
    isCollapsed: state.CollapsedReducers.isCollapsed
  }
}

const mapStateToDispatch = { // 
  CollapsedAction
}
 //引入action函数
export default connect(
    mapStateToProps,  //映射状态成为属性,是一个函数返回的是一个对象
    mapStateToDispatch //是一个对象,返回的是一个action方法。最终会转化为dispatch调用的一个函数
)(TopHeader)

mapStateToprops()

将外部的数据(即state对象)转换为UI组件的标签属性

const mapStateToprops = function (state) {
  return {
    value: state
  }
}

mapDispatchToProps()

将分发action的函数转换为UI组件的标签属性
简洁语法可以直接指定为actions对象或包含多个action方法

const mapStateToDispatch = { // 通过connect dispatch(RightListAction) 
  geiAjaxRightListAction,
  changeRightListAction,
  deleteRightListAction,
  addRightListAction
}

异步请求

例:异步请求权限列表数据

  1. 在redux|action 文件 中定义 方法:
/***** RightListAction.js ******/
import http from "../../util/http" // 自己封装的axios 

function geiAjaxRightListAction () {
// http.get 是异步发起ajax 而函数是立即调用,不会等异步回来的数据,所以在异步里面return会返回一个undefined。在http.get异步外面return也不行,数据还没取回来就return。会造成页面没有数据,因为已经渲染完了,才异步取回的数据,已经晚了。而在异步前面加return 返回出去的是一个promise对象

  return http.get("/rights?_embed=children").then(res => {
 // 业务除了:没有子菜单的也有children字段,需要把这个children删掉,否则会遍历一个空数组 ,删除对象中的一个属性,直接用delete 
    var list = res.data.map(item => {
      if (item.children.length === 0) {
        delete item.children
        return item
      }
      return item
    })

    return { // 传给 reducer中 
      type: "getdata_list",
      payload: list
    }
  })
}
  1. 组件内引入action
/*** RightList.js *****/
// 1- 引用redux 相关
import { connect } from 'react-redux'
import { geiAjaxRightListAction } from '../../../redux/ActionCreator/RightListAction'

function RightList (props) {
// 4- 组件内调用action
useEffect(() => {
    props.geiAjaxRightListAction()
  }, [])
}

// 2- 将分发action的函数转换为UI组件的标签属性
const mapStateToDispatch = { //是一个对象,返回的是一个action方法。最终会转化为dispatch调用的一个函数
  geiAjaxRightListAction
}

// 3- connect生成容器,是 RightList 的包装组件
export default connect( mapStateToDispatch)(RightList)

通过调用 geiAjaxRightListAction() 后 取回数据,然后通过reducer 赋给状态

  1. Reducer
const RightListReducer = (prevState = {
  datalist: [] //  初始状态 
}, action) => {
  let newState = { ...prevState } // { ...prevState } 展开上个状态 重新赋给一份新的状态
  let { type, payload } = action // 从action 中解构
  switch (type) {
    case "getdata_list":
      newState.datalist = payload // 状态更新
      return newState
    default:
      return prevState
  }
}

export default RightListReducer
  1. 状态更新后组件内需要把reducer 的状态拿过来映射成自己组件中的属性
// 1- 映射状态成为属性,是一个函数返回的是一个对象
const mapStateToProps = (state) => { 
  return {
    dataSource: state.RightListReducer.datalist
  }
}

// 2- 通过connect生成容器调用mapStateToProps,
export default connect(mapStateToProps, mapStateToDispatch)(RightList)

// 3- 组件内使用状态就可以直接通过props.dataSource
<Table dataSource={props.dataSource} columns={columns} />;

redux-persist持久化

基本用法:

  1. 下载:
npm i --save redux-persist
  1. 基本用法
// 1.涉及添加`persistReducer`和`persistStore`到store.js 。
import { persistStore, persistReducer } from 'redux-persist'

// 2.默认数据持久化到localStorage
import storage from 'redux-persist/lib/storage' // 默认为localStorage
const persistConfig = {
  key: 'test2008',
  storage,
}

const reducer = combineReducers({
  UserInfoReducer
})

// 把合并后的 reducer 送到 persistReducer(持久化reducer)
const persistedReducer = persistReducer(persistConfig, reducer)

const store = createStore(persistedReducer, composeEnhancers(applyMiddleware(reduxPromise)))

const persistor = persistStore(store)

export { store, persistor }
// 注,导出多个模块方式 export{},引用的时候也有 import {}
  1. 使用PersistGate包装您的根组件。这会延迟应用 UI 的呈现,直到您的持久状态被检索并保存到 redux。
// 根组件App.js
import { PersistGate } from 'redux-persist/integration/react'
// PersistGate 持久化的网关
export default function App () {
  return (
    <Provider className="App" store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <NewsRouter></NewsRouter>
      </PersistGate>
    </Provider>
  );
}
  1. 设置黑名单和白名单
// BLACKLIST
const persistConfig = {
  key: 'root',
  storage: storage,
  blacklist: ['navigation'] // navigation will not be persisted
};

// WHITELIST
const persistConfig = {
  key: 'root',
  storage: storage,
  whitelist: ['navigation'] // only navigation will be persisted
};

总结

vuex 和 redux 的区别:

  • 底层上的区别:

    vuex 底层是 getter/setter 拦截 拦截,最底层有监听。
    redux 没有监听,底层是一套订阅发布模式,需要自己监听(subscribe),自己发布(dispatch)。

  • 异步上的区别:

    vuex 直接在action 里面做异步操作

    redux 必须引用中间件(redux-promise/redux-thunk)。

  • 状态上的区别:

    vuex 原始状态必须被修改

    redux 原始状态不能被修改,需要深复制一份,只能修改新的状态。

redux缺点:

  1. 流程复杂繁琐,每次状态更新都需要 dispatch => reducer => 订阅者

  2. 每次dispatch 所有的reducer 和 所有的订阅者都会响应,都会触发一遍,如果项目里订阅者过多的话,redux占的内存就会越来越大,会影响性能问题。

  3. reducer 必须返回新状态,老状态不能受影响。所以必须要深复制。深复制存在的问题:所有状态的都会复制一遍,没有改变的状态也会受到影响被重新赋值一遍,所有有了immutable。

jq 缺点 :操作dom,需要亲自操作dom可能会出现性能问题
vue的缺点 :封装了很多语法糖,所以在操作的时候不是很自由,只能按照它规定的做,给什么用什么
react的缺点 :相对的自由,但是 什么都要自己造,用什么造什么

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值