文章目录
Flux Redux
参考自:Flux、Redux、Vuex、MobX 总结-概念篇
Flux是一种架构思想,类似于 MVC 、MVVM 等,旨在解决状态管理问题
Redux实现了Flux
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-persis
t模块
⚙️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 }