React 学习(六)

Flux Redux

参考自:Flux、Redux、Vuex、MobX 总结-概念篇

Flux是一种架构思想,类似于 MVC 、MVVM 等,旨在解决状态管理问题
在这里插入图片描述

Redux实现了Flux

img

Redux 的组成

  • Store: 存储应用 state 以及用于触发 state 更新的 dispatch 方法等,整个应用仅有**单一的 Store** 。Store 中提供了几个 API :

    • store.getState(): 获取当前 state。
    • store.dispatch(action): 用于 View 发出 Action。
    • store.subscribe(listener): 设置监听函数,一旦 state 变化则执行该函数(若把视图更新函数作为 listener 传入,则可触发视图自动渲染)。
  • Action: 同 Flux ,Action 是用于更新 state 的消息对象,由 View 发出。

    • 有专门生成 Action 的 Action Creator
  • Reducer: 是一个用于改变 state 的纯函数,即通过应用状态与 Action 推导出新的 state : (previousState, action) => newState。Reducer 返回一个新的 state 。

纯函数是指在相同的输入下,始终产生相同的输出,并且没有副作用的函数

redux也不是响应式的,需要去订阅状态变化,触发render函数,半自动

redux库是纯js构建的,因此可以在react、vue等框架使用

⚙️ 安装

npm i redux

🔧 使用

state是只读的

使用纯函数reducer执行state更新

//> /src/redux/store.js

// 引入redux
// createStore(reducer)

import { createStore } from 'redux'

//纯函数 reducer
//prevState 老状态, action响应更新state
const reducer = (prevState = {
  	//定义的状态
    show: true,
    cityname : '北京'
}, action = {}) => {
  	// 不对本身的状态进行直接修改,深克隆一份,对其进行处理
    const newState = { ...prevState }
    
    //根据dispatch来的不同type类型对状态进行修改
    switch (action.type) {
        case 'hide':
            newState.show = false
            return newState
        case 'show':
            newState.show = true
            return newState
        case 'change-city':
            newState.cityname = action.payload
            return newState
        default:
        		// 没有dispatch处理,默认返回老状态
            return prevState
    }
}
// 注册store
const store = createStore(reducer)

//导出 store
export default store
//导入store
import store from '路径/redux/store'
//在App组件中使用
function App(){
  useEffect(()=>{
    //********//
    store.subscribe(()=>{
      console.log(store.getState()) //订阅-获取state
    })
  })
  return (...)
}


// 在City组件中使用
function City(){
  useEffect(()=>{
    //********//
    //派发任务给action
    store.dispatch(()=>{
      type: 'change-city',
      payload: '上海'
    })
  })
  return (...)
}
actionCreator

Action Creator 是 Redux 中的一个概念,它是一个函数,用于创建并返回一个 Action 对象

//> src/redux/actionCreator.js

const hide = () => ({type: 'hide'})
const show = () => ({type: 'show'})
const changeCity = () => ({type: 'change-city', payload: '上海'})

export {
	hide,
  show,
  changeCity
}
// 在使用时,dispatch派发任务
import { changeCity } from '路径/actionCreator'
...
store.dispatch(changeCity)
...
Redux原理

订阅发布模式(简单模拟)

function createMyStore(reducer) {
    const state = reducer()
    const list = []

    const subscribe = (callback) => {
        list.push(callback)
    }
    const dispatch = (action) => {
        state = reducer(state ,action)
        list.forEach(cb => {
            cb && cb()
        })
    }

    const getState = () => {
        return state
    }
    return {
        subscribe,
        dispatch,
        getState
    }
}
reducer拆分合并

Reducer 负责处理状态的变化,并返回新的状态。

当应用的状态较为复杂时,可以将 Reducer 拆分成多个小的 Reducer,然后通过合并这些小的 Reducer 形成一个根 Reducer。

  • 拆分reducer
//> src/redux/reducers/CityReducer.js
// 引入redux
//  createStore(reducer)
import { createStore } from 'redux'
const CityReducer = (prevState = {
    cityname: '北京'
}, action = {}) => {
    const newState = { ...prevState }
    switch (action.type) {
        case 'change-city':
            // console.log(newState, 'sajsnaniasn')
            newState.cityname = action.payload
            return newState
        default:
            return prevState
    }

}
//导出CityReducer
export default CityReducer

//> src/redux/reducers/TestReducer.js
...类似的再创一个TestReducer
  • 合并reducer

​ 在默认的store文件下,借助于redux的combineReducers函数

//> /src/redux/store.js
// 引入redux
//  createStore(reducer)
import { combineReducers, createStore } from 'redux'

import CityReducer from './reducers/CityReducer'
import TabbarReducer from './reducers/TabbarReducer'
//**************合并reducer*******************//
const reducer = combineReducers({
    CityReducer, 
    TestReducer
})

const store = createStore(reducer)

export default store

因为有了新的命名空间,访问其中的某个reducer里的state数据需要store.getState().xxxReducer

actionCreator里写异步

Redux中间件

在redux里action仅仅是携带了数据的普通对象,而actionCreator返回的值也仅仅是这个action类型的普通对象,然后通过dispatch派发。

我们需要用redux进行缓存数据,但是这个数据是异步(ajax)请求回来的。

但是reducer,无法处理异步的情况

//> xxxactionCreator
// 这种异步方式,不能及时获取数据
const getListAction = () => {
  axiost.get('xxx').then(res=>{
    return {
      type: 'change-list',
      payload: res.data
    }
  })
}

需要在action和reducer之间用中间件middlware来处理异步

应用中间件applyMiddleWare 来使用中间件

redux-thunk常用异步中间件

redux-promise Promise风格的

⚙️下载 npm i redux-thunk redux-promise

// redux/store.js
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

//合并reducer
const reducer = combineReducers({
    xReducer,
    yabbarReducer,
    zReducer
})
//!! 应用中间件(可以应用多个)
const store = createStore(reducer, applyMiddleware(reduxThunk, reduxPromise)) 

💬针对actionCreator

同步默认方式, 直接return一个对象

export function TestAction() {
  return {
    type: 'test',
    payload: 'testvalue'
  }
}

异步使用中间件处理

使用redux-thunk 导出函数式

export function TestAction() {
  return (dispatch) => {
    axios.get('xxx').then(res=>{
      // *异步请求完了才会dispatch(action)*
      dispatch({
        type: 'test',
        payload: 'testvalue'
      })
    })
  }
}

redux-promise Promise风格

export function TestAction() {
  axios.get('xxx').then(res=>{
     return {
       type: 'test',
       payload: res
     } 
    })
}

async风格

export async function TestAction() {
  const res = await axios.get('xxx')
  return {
    type: 'test',
    payload: res
  }
}
redux开发者工具

浏览器安装 redux-devtools

项目 npm install redux-devtools-extension -D

//> redux/store.js
import { composeWithDevTools } from 'redux-devtools-extension'
...
const store = createStore(reducer, composeWithDevTools(applyMiddleware(reduxThunk, reduxPromise)))

🟢react-redux (基于redux)

⚙️下载npm i react-redux

不用手动disptach,不用手动subscribe以及unsubscribe

父传子 props

子传父 回调

Provider组件给整个向组件树中注入同一个store,后续通过connext()连接Provider能访问到store

//> index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
// !!!
import store from '路径/redux/store'
import { Provider } from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
    	<App />
    </Provider>
);

connect函数生成一个父组件(高阶组件)

import { connect } from 'react-redux'
function Foo(props){
  // props中直接接受到connect加工过来的属性及方法
  console.log(props)
  useEffect(()=>{
    console.log(props.data)
    props.testAction() //直接调用这个方法相当与dispatch了这个action
  }, [props.data, props.testAction])
  return (
  	<div>一个组件</div>
  )
}
//connect高阶组件加工当前Foo组件,向其注入props
export default connect(/*其中的参数表示如下*/)(Foo)

connect第一个函数调用接受两个参数, 一个回调函数,一个对象

connect((state)=>{  //回调接受一个全局state参数,就不用另外导入store了
  return {
    //给Foo传递的属性
    data: state,
    moreData: '...'
  }
},{
  //给Foo传递的方法
  testAction(){
    return { //这个方法return的是一个action,connect自动disptch
      type: '',
      payload: ''
    }
  },
  moreOtherAction(){/*...*/}
})
//可重新命名
const mapStateToProps = (state) => {
  return {
    value: state.testVal
  }
}
const mapDispatchToProps = {
  testAction(){
    return {
      type: '',
      paload: ''
    }
  },
  otherAction //导入外部actionCreator生成的action
}
export default connect(mapStateToProps, mapDispatchToProps)(Foo)  // 如果仅需填写第二个参数,第一个参数null指定
React-redux原理

connect是HOC高阶组件

Provider组件,向组件树中注入同一个store,后续通过connext()连接Provider能访问到store

HOC不仅仅是一个方法,还是一个组件工厂,获取低阶组件,将其加工成高阶组件

代码复用、增删改props、渲染劫持

connect 将ui组件–转换成-> 容器组件

connect创建的容器组件用于连接 Redux 的状态和操作到 React 组件中,以便在组件中可以方便地访问和更新 Redux 的状态

  • UI组件
    • 负责**UI呈现、**没有任何业务逻辑
    • 没有状态
    • 使用的数据由props提供
    • 不使用任何Redux的API
  • 容器组件
    • 负责管理数据业务逻辑
    • 带有内部状态
    • 使用Redux
// 模拟一个connect高阶组件
const MyConnect = (cb, actionsObj) => {
  const value = cb() //执行回调拿到数据

  // 返回一个函数,传入待加工的组件
  return (Component) => {
    
    // return一个函数式组件
    return (props) => {
      console.log('props', props)//Component组件的props属性
      return (
        <div style={{ color: 'red' }}>
          {/**********/}
          <Component {...value} {...props} {...actionsObj} />
        </div>
      )
    }
  }
}

export default MyConnect(() => {
  return {

  }
}, {

})(NotFound)


Redux-thunk持久化缓存

利用redux-persist模块

⚙️npm install redux-persist

//> /redux/store.js
import { applyMiddleware, combineReducers, createStore } from 'redux'

import CityReducer from './reducers/CityReducer'
import TabbarReducer from './reducers/TabbarReducer'
import CinemalistReducer from './reducers/CinemalistReducer'

// >中间件
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'


// >持久化redux
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web

// >合并reducer
const reducer = combineReducers({
    CityReducer,
    TabbarReducer,
    CinemalistReducer
})

const persistConfig = {
    key: 'myKey',
    storage, // 存入localStorage
    blacklist: ['CityReducer'] // CityReducer 将不被被持久化
}
const persistedReducer = persistReducer(persistConfig, reducer)

const store  = createStore(persistedReducer, applyMiddleware(reduxThunk, reduxPromise))
const persistor = persistStore(store)

export { store, persistor } 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值