Redux

全局状态管理


Redux


Redux的设计思想:

Web 应用是一个状态机,视图与状态是一一对应的。
所有的状态,保存在一个对象里面(唯一数据源)。

Redux的使用的三大原则:

Single Source of Truth(唯一的数据源)
State is read-only(状态是只读的)
Changes are made with pure function(数据的改变必须通过纯函数完成)


Redux的使用


新建store.js用于创建store全局状态容器

// store.js

import { createStore } from 'redux'

// 在redux当中 creatStore 传入一个函数 这个函数被称为reducer
// 每一次reducer执行 返回值就是最新的全局store

const store = createStore((state,actions) => {
  // state 是上一次返回的state的值
  // actions 就是dispath当中传递的参数
  return {
    a:1,
    b:2
  }
})

export default store

导出store后 其他组件引入即可以共享状态

// app.js

import store from './store' // 引入store 全局状态

export default function app () {
  return (
      <div>
        <h1>app根组件</h1>
        <hr />
        <Test></Test>
      </div>
  )
}

通过store的api来进行订阅发布

store.getState() 获取store中的状态对象

store.subscribe() 订阅 接收一个函数作为参数 当dispatch触发时执行接收的回调函数 类似事件绑定 因此需要解绑
store.subscribe() 的返回值为一个函数 这个函数用于解绑订阅 在需要解绑时调用

store.dispatch() 发布 接收一个对象作为参数 这个参数中必须有type条目 会触发store中的reducer 并将对象传入reducer的action中


对于redux而言 如果一个函数最终返回的结果是一个action格式的内容 那么这个函数可以称之为 action creator 也成为action创造函数




Redux的Reducer合并


import { combineReducers, createStore } from 'redux'

// reducer函数1
const reducer1 = (state={ a:1, b:2 },actions) => {
  return state
}
// reducer函数2
const reducer2 = (state={ a:1, b:2 },actions) => {
  return state
}
// 通过combineReducers将多个reducer函数合并为一个reducer
const reducer = combineReducers({
  reducer1,
  reducer2
})
// 当访问属性时需要props.reducer1.a来获取
const store = createStore(reducer)

export default store



React-redux


创建全局状态管理

// app.js

import { Provider } from 'react-redux'  // 引入Provider组件用于向组件树传递数据 
import store from './store'

export default function app () {
  return (
    <Provider store={store}> 			// 将创建好的store全局状态传入
      <div>
        <h1>app根组件</h1>
        <hr />
        <Test></Test>
      </div>
    </Provider>
  )
}

类组件

同样可以用于函数组件 (推荐)

// test2.js 类组件中

import { Component } from 'react'
import { connect } from 'react-redux'  // 引入 connect 方法

class Test2 extends Component {
  render () {
    console.log(this.props);
    return (
      <div>
        <h1>Test2组件</h1>
      </div>
    )
  }
}
const map = (state) => {
  // return state
  return {  // return 的对象就是Test2接收到的对象
    a:state.a
  }
}
// 通过 connect 可以把store里面返回的状态全部挂载到组件的 props 属性上面
export default connect(map)(Test2)

通过connect高阶组件封装的子组件会将connect的参数传递给子组件

类组件的dispatch

// test2.js 类组件中

import React, { Component } from 'react'
import { connect } from 'react-redux'

class Test2 extends Component {
//-----------------dispatch方式1 不推荐---------------------------------

  fn = () => {
    this.props.dispatch({     	// 可以通过dispatch来分发一个 actions
      a: 1,						// dispatch实际上就是让 store 里面的 reducer 执行
      type: 'asd'				// 传入的参数就是 actions 
      							// 传入参数时必须携带type (可以在store.js中根据传入的actions中的type进行业务逻辑处理)
    })
  }
  
  render () {
    return (
      <div>
		<button onClick={this.fn}>dispatch</button>	{/* 方式1的调用 */}
        <button onClick={this.props.fn1}>dispatch</button> {/* 方式2的调用 */}
        <h1>Test2组件</h1>
      </div>
    )
  }
}
  
const mapStateToProps = (state) => {
  // return state
  return {
    a: state.a
  }
}

//-----------------dispatch方式2 推荐---------------------------------

// 在高阶组件connect封装组件后会将属性传递给子组件 也会自动分发action 所以不需要自己调用dispatch
const mapDispatchToProps = {
  fn1(){
  	 return { type:'cc', payload:1 }  // 直接return一个action对象 在connect中传入
  }
}

// 通过 connect 可以把store里面返回的状态全部挂载到组件的props属性上面
// connect调用接收两个参数 第一个是映射的属性 第二个是映射的dispatch 也可以简写为对象形式{fn1}

export default connect(mapStateToProps,mapDispatchToProps)(Test2)
//--------------------------------------------------------------------



函数组件

通过自定义hook(不推荐)

// test.js 函数组件中

import Test2 from './test2'   // 嵌套的子组件
import { useSelector } from 'react-redux' 	// 引入 redux 中的 hook useSelector

export default function Test() {
  const s = useSelector((state) => {		// useSelector接收一个函数作为参数 函数return的值为变量接收到的值
  
    // return state							// state为一整个store对象 相当于返回全局状态的所有值
    
    return {
    	a:state.a							// 获取全局状态中的部分值
  	}
  })
  console.log(s);
  return (
    <div>
      <h1>Test1 组件</h1>
      <hr />
      <Test2></Test2>
    </div>
  )
}

函数组件的dispatch

// test.js 函数组件中

import Test2 from './test2'
import { useSelector ,useDispatch } from 'react-redux'

export default function Test() {
  const dispatch = useDispatch() 	// 通过引入 hook useDispatch 来调用 dispatch 方法
  const fn = () => {				// 然后通过dispatch来分发一个 actions
    dispatch({						// 传入的参数就是 actions
      c:5,
      type:'cde'					// 传入参数时必须携带type (可以在store.js中根据传入的actions中的type进行业务逻辑处理)
    })
  }
  return (
    <div>
      <button onClick={fn}>dispatch</button>
      <h1>Test1 组件</h1>
      <hr />
      <Test2></Test2>
    </div>
  )
}

:dispatch的调用更新state 表现为 异步更新

从发送action到redux内的state更新这一过程是同步的,具体可参考同步数据流。

那么为什么在具体体现上是异步的呢,其实是由于React的setState在同步代码中是异步的。

可以通过React的Devtools观察到,通过react-redux连接器封装后的组件,其实就是在组件外层包了一层高阶组件。

而这一个高阶组件在redux里的state更新时会调用setState。所以,整个过程就会呈现异步的形式。




Redux中间件

在redux中,action仅仅是携带了数据的普通js对象,action creator返回的这个action类型的对象

然后通过store.dispath()进行分发。同步的情况下一切都很完美,但是reducer无法处理异步的情况

这时就需要在action与reducer中应用一个中间件,middleware

redux-thunk
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const reducer = (state = { a: 1, b: 2 }, actions) => {
  return state
}

const store = createStore(reducer, applyMiddleware( thunk ))

export default store

应用了redux-thunk中间件后 action可以为一个函数

fn1 = () => {
    return () => {
      store.dispatch({ type: 'sb' }) // 可以发送ajax请求得到数据后dispatch数据
    }
  }
  
// 调用
store.dispatch(this.fn1())



redux-promise
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import promise from 'redux-promise'

const reducer = (state = { a: 1, b: 2 }, actions) => {
  return state
}

const store = createStore(reducer, applyMiddleware( thunk, promise ))

export default store

应用了redux-promise中间件后 action可以为一个promise

// 通过async await进行优化
fn1 = async () => {
    const res = await Promise.resolve(1).then((reslove) => {
      return {res:reslove,type:'cc'}
    })
    return res
}

// 调用
store.dispatch(await this.fn1())



Redux持久化


当需要将redux中的数据持久化到localStorage中时,需要用到redux-persist
// store.js

import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist' 
import storage from 'redux-persist/lib/storage' // 如果应用最后通过cordova或uniapp封装在app优先考虑localStorage
import sessionStorage from 'redux-persist/es/storage/session' // 如果是微信公众号或web(h5)应用 一般使用sessionStorage

const persistConfig = { // 持久化配置
  key:'cc',  // key为存入内存中数据的键
  storage,    // 存入localStorage
  // blacklist:['b'],  黑名单 判断哪些值不需存入内存中
  // whitelist:['b'],  白名单 判断哪些值需要存入内存中 黑名单和白名单不能同时使用
}

// 定义的reducer函数
const reducer = (state = { a: 1, b: 2 }, actions) => {
  return state
}

const persistedReducer = persistReducer( persistConfig, reducer )  // 使用持久化配置来持久化reducer

const store = createStore(persistedReducer) // 使用持久化的reducer来创建store

const persistor = persistStore(store)   // 使用持久化的store来创建持久化存储

export { store, persistor } // 将持久化store和持久化存储进行暴露
// app.js
// 用PersistGate组件将持久化存储传入组件树中 以及持久化store
import ReactDOM from 'react-dom';
import App from './components/app.js'
import { store, persistor } from './components/store'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/es/integration/react'


ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App></App>
    </PersistGate>
  </Provider>,
  document.getElementById('root')
);

修改后store全局状态会同步存储到localStorage中


Redux调试工具

import { createStore } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'

const reducer = (state={ a:1, b:2 },actions) => {
  return state
}
const store = createStore(reducer,composeWithDevTools())

export default store




使用:

import { combineReducers, createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import promise from 'redux-promise'
import { composeWithDevTools } from 'redux-devtools-extension'
import { persistStore, persistReducer } from 'redux-persist' 
import storage from 'redux-persist/lib/storage' //defaults to localStorage for web
import sessionStorage from 'redux-persist/es/storage/session'

const persistConfig = { // 持久化配置
  key:'cc',  // key为存入localStorage中数据的键
  storage,    // 存入localStorage
  // blacklist:['b'], 	// 黑名单
  // whitelist:['b'],	// 白名单
}

// 定义的reducer函数
const reducer1 = (state={ a:1, b:2 },actions) => {
  return state
}
const reducer2 = (state={ a:1, b:2 },actions) => {
  return state
}
const reducer = combineReducers({
  reducer1,
  reducer2
})

const persistedReducer = persistReducer( persistConfig, reducer )  // 使用持久化配置来持久化reducer
const store = createStore(persistedReducer,composeWithDevTools(applyMiddleware( thunk, promise ))) 
// 使用持久化的reducer来创建store 并使用调试工具 以及中间件thunk和promise
const persistor = persistStore(store)   // 使用持久化的store来创建持久化存储
export { store, persistor } // 将持久化store和持久化存储进行暴露
import ReactDOM from 'react-dom';
import App from './components/app.js'
import { store, persistor } from './components/store'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/es/integration/react'


ReactDOM.render(
  <Provider store={store}>
     <PersistGate loading={null} persistor={persistor}>
      <App></App>
     </PersistGate>
   </Provider>,
  document.getElementById('root')
);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Raccom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值