共计 15589 个字符,预计需要花费 39 分钟才能阅读完成。
React 状态管理工具
Redux
1. Redux 外围
1.1 Redux 介绍
JavaScript 状态容器,提供可预测化的状态治理
1.2 Redux 外围概念及流程
Store: 存储状态的容器,JavaScript 对象
View: 视图,HTML 页面
Actions: 对象,形容对状态进行怎么的操作
Reducers: 函数,操作状态并返回新的状态
1.3 Redux 应用:计数器案例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Redux</title>
</head>
<body>
<button id="minus">-</button>
<span id="count">0</span>
<button id="plus">+</button>
<script src="./redux.min.js"></script>
<script>
// 3. 存储默认状态
const initialState = {count: 0}
// 2. 创立 reducer 函数
function reducer(state = initialState, action) {switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
return state;
}
}
// 1. 创立 store 对象
const store = Redux.createStore(reducer);
// 4. 定义 action
const increment = {type: 'increment'}
const decrement = {type: 'decrement'}
// 5. 获取按钮 给按钮增加点击事件
document.getElementById('minus')
.addEventListener('click', function () {
// 6. 获取 dispatch 触发 action
store.dispatch(decrement)
})
document.getElementById('plus')
.addEventListener('click', function () {
// 6. 获取 dispatch 触发 action
store.dispatch(increment)
})
// 获取 store {dispatch: ƒ, subscribe: ƒ, getState: ƒ, replaceReducer: ƒ, Symbol(observable): ƒ}
console.log(store)
// 获取 store 中存储的状态 state
console.log(store.getState());
// 订阅数据的变动
store.subscribe(() => {console.log(store.getState())
document.getElementById('count').innerHTML = store.getState().count});
</script>
</body>
</html>
1.4 Redux 外围 API
// 创立 store 对象
const store = Redux.createStore(reducer);
// 2. 创立 reducer 函数
function reducer(state = initialState, action) {switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
return state;
}
}
// 获取 store 中存储的状态 state
store.getState()
// 订阅数据的变动
store.subscribe(() => {console.log(store.getState())
});
// 获取 dispatch 触发 action
store.dispatch(increment)
2. React + Redux
2.1 在 React 中不应用 Redux 时遇到的问题
在 React 中组件通信的数据流是单向的,顶层组件能够通过 Props 属性向上层组件传递数据,而上层组件不能向下层组件传递数据。要实现上层组件批改数据,须要下层组件传递批改数据的办法到上层组件。等我的项目越来越大的时候,组件间传递数据变得越来越艰难。
2.2 在 React 我的项目中退出 Redux 的益处
应用 Redux 治理数据,因为 Store 独立于组件,使得数据管理独立于组件,解决了组件与组件之间传递数据艰难的问题。
2.3 下载 Redux
npm install redux react-redux
2.4 Redux 工作流程
- 组件通过 dispatch 办法触发 Action
- Store 承受 Action 并将 Action 分发给 Reducer
- Reducer 依据 Action 类型对状态进行更改并将更改后的状态返回给 Store
- 组件订阅了 Store 中的状态,Store 中的状态更新会同步到组件
2.5 Redux 应用步骤
2.5.1 创立 store
// src/store/index.js
import {createStore} from 'redux'
import reducer from './reducers/counter.reducer'
export const store = createStore(reducer)
在根组件中应用store
:
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './components/Counter'
import {Provider} from 'react-redux'
import {store} from './store'
/**
* react-redux
* Provider
* connect
*/
ReactDOM.render(
// 通过 provider 组件,将 store 放在了全局的组件能够够得着的中央
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
);
2.5.2 创立 reducer
// src/store/reducers/counter.reducer.js
import {DECREMENT, INCREMENT} from "../count/counter.const";
const initialState = {count: 0}
export default function reducer (state = initialState, action) {switch (action.type) {
case INCREMENT:
return {count: state.count + 1};
case DECREMENT:
return {count: state.count - 1};
default:
return state;
}
}
// src/store/count/counter.const.js
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
2.5.3 在组件中应用 connect 承受 store 外面的 state 和 dispatch
connect
办法承受两个参数,返回一个高阶组件。connect
办法的第一个参数是 mapStateToProps
办法,将 store 中的 state 传递到组件的 props
中,mapStateToProps
办法的参数是state
,返回值是一个对象,会传递到组件中,写法如下:
const mapStateToProps = (state) => ({
count: state.count,
a: 'a', // 这里怎么定义,组件中就能够或失去一个属性
})
connect
办法的第二个参数是 mapDispatchToProps
办法,将 store
中的 dispatch
传递到组件的 props
中,mapDispatchToProps
办法的参数是dispatch
,返回值是一个对象,对象中的办法能够应用dispatch
, 这个对象中的办法会传递到组件中,写法如下:
const mapDispatchToProps = (dispatch) => ({increment () {dispatch({ type: 'increment'})
},
decrement () {dispatch({ type: 'decrement'})
}
})
此外,咱们还能够通过 redux
中的 bindActionCreators
来帮咱们创立 action
函数:
import {bindActionCreators} from 'redux'
// bindActionCreators 会返回一个对象
const mapDispatchToProps = dispatch => (
// 解构
...bindActionCreators({increment () {return { type: 'increment'}
},
decrement () {return { type: 'decrement'}
}
}, dispatch)
)
或者写成:
const mapDispatchToProps = dispatch => bindActionCreators({increment () {return { type: 'increment'}
},
decrement () {return { type: 'decrement'}
}
}, dispatch)
也能够将 bindActionCreators
的第一个参数进行抽离:
import * as counterActions from '../store/actions/counter.actions'
const mapDispatchToProps = dispatch => bindActionCreators(conterActions, dispatch)
// src/store/actions/counter.actions.js
import {DECREMENT, INCREMENT} from "../count/counter.const"
export const increment = () => ({type: INCREMENT})
export const decrement = () => ({type: DECREMENT})
connect
办法承受 mapStateToProps
和mapDispatchToProps
,返回一个高阶组件,而后传入 Counter
组件进行导出:
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
最终组件代码如下:
// src/components/Counter.js
import React from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import * as counterActions from '../store/actions/counter.actions'
function Counter ({count, increment, decrement}) {
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
)
}
// 1. connect 会帮忙咱们去订阅 store,当 store 中的状态产生了变动后,能够帮咱们从新渲染组件
// 2. connect 办法能够让咱们获取 store 中的状态,将状态通过组建的 props 属性映射给组件
// 3. connect 办法能够让咱们获取 dispatch 办法
const mapStateToProps = (state) => ({
count: state.count,
a: 'a', // 这里怎么定义,组件中就能够或失去一个属性
})
const mapDispatchToProps = dispatch => bindActionCreators(counterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
2.5.4 为 action 传递参数
-
传递参数
<button onClick={() => increment(5)}> + 5</button>
-
承受参数,传递
reducer
export const increment = payload => ({type: INCREMENT, payload}) export const decrement = payload => ({type: DECREMENT, payload})
-
reducer
依据承受收到的数据进行解决export default function reducer (state = initialState, action) {switch (action.type) { case INCREMENT: return {count: state.count + action.payload}; case DECREMENT: return {count: state.count - action.payload}; default: return state; } }
2.6 redux 实现弹出框案例
store
中的状态越多,reducer
中的 switch
分支就会越多,不利于保护,须要拆分reducer
src/index.js
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux'
import {store} from './store'
ReactDOM.render(
// 通过 provider 组件,将 store 放在了全局的组件能够够得着的中央
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
src/store/index.js
// src/store/index.js
import {createStore} from 'redux'
import reducer from './reducers/counter.reducer'
export const store = createStore(reducer)
src/store/reducers/counter.reducer.js
// src/store/reducers/counter.reducer.js
import {DECREMENT, INCREMENT} from "../const/counter.const";
import {HIDEMODAL, SHOWMODAL} from "../const/modal.const";
const initialState = {
count: 0,
show: false
}
export default function reducer (state = initialState, action) {switch (action.type) {
case INCREMENT:
return {...state, count: state.count + action.payload};
case DECREMENT:
return {...state, count: state.count - action.payload};
case SHOWMODAL:
return {...state, show: true};
case HIDEMODAL:
return {...state, show: false};
default:
return state;
}
}
src/App.js
// src/App.js
import Modal from './components/Modal'
import Counter from './components/Counter'
function App() {
return (
<div className="App">
<Counter />
<Modal />
</div>
);
}
export default App;
src/components/Modal.js
// src/components/Modal.js
import React from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import * as modalActions from '../store/actions/modal.actions'
function Modal ({showStatus, show, hide}) {
const styles = {
display: showStatus ? 'block': 'none',
width: 200,
height: 200,
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
margin: 'auto',
backgroundColor: 'skyblue'
}
return (
<div>
<button onClick={show}> 显示 </button>
<button onClick={hide}> 暗藏 </button>
<div style={styles}></div>
</div>
)
}
const mapStateToProps = state => ({showStatus: state.show})
const mapDispatchToProps = dispatch => bindActionCreators(modalActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Modal)
src/store/actions/modal.action.js
// src/store/actions/modal.action.js
import {HIDEMODAL, SHOWMODAL} from "../const/modal.const"
export const show = () => ({ type: SHOWMODAL})
export const hide = () => ({ type: HIDEMODAL})
src/store/const/modal.const.js
// src/store/const/modal.const.js
export const SHOWMODAL = 'showModal'
export const HIDEMODAL = 'hideModal'
2.7 拆分reducer
应用 reducer
提供的工具 combineReducers
合并每一个小的reducer
src/store/reducers/root.reducer.js
// src/store/reducers/root.reducer.js
import {combineReducers} from 'redux'
import CounterReducer from './counter.reducer'
import ModalReducer from './modal.reducer'
// {counter: { count: 0}, modal: {show: false} }
export default combineReducers({
counter: CounterReducer,
modal: ModalReducer
})
src/store/reducers/counter.reducer.js
// src/store/reducers/counter.reducer.js
import {DECREMENT, INCREMENT} from "../const/counter.const";
const initialState = {count: 0,}
export default function counterReducer (state = initialState, action) {switch (action.type) {
case INCREMENT:
return {...state, count: state.count + action.payload};
case DECREMENT:
return {...state, count: state.count - action.payload};
default:
return state;
}
}
src/store/reducers/modal.reducer.js
// src/store/reducers/modal.reducer.js
import {HIDEMODAL, SHOWMODAL} from "../const/modal.const";
const initialState = {show: false}
export default function modalReducer (state = initialState, action) {switch (action.type) {
case SHOWMODAL:
return {...state, show: true};
case HIDEMODAL:
return {...state, show: false};
default:
return state;
}
}
创立 store
时传入的 reducer
则来自于咱们方才定义的root.reducer.js
import {createStore} from 'redux'
import RootReducer from './reducers/root.reducer'
export const store = createStore(RootReducer)
在每个组件中的 mapStateToProps
中也要产生相应的扭转 (state.counter
和state.modal
):
const mapStateToProps = (state) => ({count: state.counter.count,})
const mapStateToProps = state => ({showStatus: state.modal.show})
3. Redux 中间件
3.1 什么是中间件
中间价容许咱们扩大和加强 redux 应用程序
3.2 开发 Redux 中间件
开发中间件的模板
export default store => next => action => {}
3.3 注册中间件
中间件在开发实现当前只有被注册能力在 Redux 的工作流程中失效
src/store/index.js
// src/store/index.js
import {createStore, applyMiddleware} from 'redux'
import logger from './middlewares/logger'
createStore(reducer, applyMiddleware(logger))
src/store/middleware/logger.js
const logger = store => next => action => {console.log(store)
console.log(action)
next(action) // 千万别忘了调用 next(action)
}
export default logger
如果注册多个中间件,中间件的执行程序就是注册程序,如:
createStore(reducer, applyMiddleware(
logger,
test
))
那么执行程序就是先执行 logger
中间件,再执行 test
中间件。
如果中间件中的结尾不调用next(action)
,则整个流程就会卡在此处不会再往后执行了
3.4 Redux 中间件开发实例 thunk(异步中间件)
以后这个中间件函数不关怀你想执行什么样的异步操作,只关怀你执行的是不是异步操作,
如果你执行的是异步操作,你在触发 action 的时候,给我传递一个函数,如果执行的是同步操作,就传递一个 action 对象,
异步操作代码要写在你传进来的函数中
当这个中间件函数,在调用你传进来的函数时要将 dispatch 办法传递过来
src/store/middleware/thunk.js
// src/store/middleware/thunk.js
import {DECREMENT, INCREMENT} from "../const/counter.const";
const thunk = ({dispatch}) => next => action => {if (typeof action === 'function') {return action(dispatch) // action 办法外部会发动新的 dispatch
}
next(action)
}
export default thunk
在 action 文件中定义异步函数 action:
src/store/actions/modal.actions.js
// src/store/actions/modal.actions.js
import {HIDEMODAL, SHOWMODAL} from "../const/modal.const"
export const show = () => ({ type: SHOWMODAL})
export const hide = () => ({ type: HIDEMODAL})
export const show_async = () => dispatch => {setTimeout(() => {dispatch(show())
}, 2000);
}
本来应用 show
的中央,当初改用show_async
, 实现了异步的性能
4. Redux 罕用中间件
4.1 redux-thunk
4.1.1 redux-thunk 下载
npm install redux-thunk
4.1.2 引入 redux-thunk
import thunk from 'redux-thunk';
4.1.3 注册 redux-thunk
import {applyMiddleware} from 'redux'
createStore(rootReducer, applyMiddleware(thunk));
4.1.4 应用 redux-thunk 中间件
const loadPosts = () => async dispatch => {const posts = await axios.get('/api/posts').then(response => response.data);
dispatch({type: LOADPOSTSSUCCE, payload: posts});
}
4.2 redux-saga
4.2.1 redux-saga 解决的问题
redux-saga
能够将异步操作从 Action Creator
文件中抽离进去,放在一个独自的文件中。
4.2.2 下载 redux-saga
npm install redux-saga
4.2.3 创立 redux-saga 中间件
src/store/index.js
// src/store/index.js
import createSagaMiddleware from 'redux-saga';
const sagaMiddleware = createSagaMiddleware();
4.2.4 注册 sagaMiddleware
src/store/index.js
// src/store/index.js
createStore(reducer, applyMiddleware(sagaMiddleware))
4.2.5 应用 saga 承受 action 异步执行操作
src/store/sagas/counter.saga.js
// src/store/sagas/counter.saga.js
import {takeEvery, put, delay} from 'redux-saga/effects'
import {increment} from '../actions/counter.actions'
import {INCREMENT_ASYNC} from '../const/counter.const'
// takeEvery 接管 action
// put 触发 action
function * increment_async_fn (action) {yield delay(2000) // 此处会暂停 2 秒钟
yield put(increment(action.payload))
}
export default function * counterSaga () {
// 接管 action
yield takeEvery(INCREMENT_ASYNC, increment_async_fn) // 第二个函数形参会承受一个 action 函数
}
src/store/actions/counter.actions.js
// src/store/actions/counter.actions.js
// 给 saga 应用
export const increment_async = (payload) => ({type: INCREMENT_ASYNC, payload});
src/store/const/counter.const.js
// src/store/const/counter.const.js
export const INCREMENT_ASYNC = 'increment_async'
src/components/Counter.js
<button onClick={() => increment_async(20)}>+</button>
4.2.6 启动 saga
src/store/index.js
// src/store/index.js
import counterSaga from './sagas/counter.saga'
sagaMiddleware.run(counterSaga);
4.2.7 合并 saga
src/store/saga/root.saga.js
// src/store/saga/root.saga.js
import {all} from 'redux-saga/effects'
import counterSaga from './counter.saga'
import modalSaga from './modal.saga'
export default function * rootSaga () {
yield all([counterSaga(),
modalSaga()])
}
modal.saga.js 没变,modal.saga.js 如下
src/store/saga/modal.saga.js
// src/store/saga/modal.saga.js
import {takeEvery, put, delay} from 'redux-saga/effects'
import {show} from '../actions/modal.actions'
import {SHOWMODAL_ASYNC} from '../const/modal.const'
// takeEvery 接管 action
// put 触发 action
function * showModal_async_fn () {yield delay(2000)
yield put(show())
}
export default function * modalSaga () {
// 接管 action
yield takeEvery(SHOWMODAL_ASYNC, showModal_async_fn)
}
store 入口文件中的 saga 中间件启动 root.saga
src/store/index.js
// src/store/index.js
import rootSaga from './sagas/root.saga'
sagaMiddleware.run(rootSaga)
4.3 redux-actions
4.3.1 redux-actions 解决的问题
redux 流程中大量的样板代码读写很苦楚,应用 redux-action 能够简化 Action 和 Reducer 的解决
4.3.2 redux-action 下载
npm install redux-actions
4.3.3 创立 Action
import {createAction} from 'redux-actions'
const increment_action = createAction('increment');
const decrement_action = createAction('decrement');
4.3.4 创立 Reducer
src/store/actions/counter.actions.js
src/store/actions/counter.actions.js
// 应用 redux-actions
import {createAction} from 'redux-actions'
export const increment = createAction('increment')
export const decrement = createAction('decrement')
src/store/reducers/counter.reducer.js
// src/store/reducers/counter.reducer.js
import {handleActions as createReducer} from 'redux-actions'
import {increment, decrement} from '../actions/counter.actions'
const initialState = {count: 0,}
const handleIncrement = (state, action) => ({count: state.count + action.payload})
const handleDecrement = (state, action) => ({count: state.count - action.payload})
export default createReducer({[increment]: handleIncrement,
[decrement]: handleDecrement,
}, initialState)
组件应用:
src/components/Counter.js
// src/components/Counter.js
function Counter ({count, increment, decrement}) {
return (
<div>
<button onClick={() => decrement(1)}>-</button>
<span>{count}</span>
<button onClick={() => increment(1)}>+</button>
</div>
)
}
redux-actions 也能够联合在 redux-saga 中应用
MobX
1. Mobx 简介
1.1 Mobx 介绍
简略,可扩大的状态治理库
Mobx 是由 Mendix(代码开发平台), Coinbase(比特币公司), Facebook 开源和泛滥集体赞助商所资助的
React 和 Mobx 是一对强力组合,React 负责渲染利用状态,Mobx 负责管理利用状态供 React 应用
1.2 MobX 浏览器反对
MobX 5 版本运行在任何反对 ES6 Proxy 的浏览器,不反对 IE11,Node.js 6
MobX 4 能够运行在任何反对 ES5 的浏览器上
MobX 4 和 5 的 API 是雷同的
2. 开发前的筹备
2.1 启用装璜器语法反对(形式一)
- 弹射我的项目底层配置:
npm run eject
- 下载装璜器语法 babel 插件:
npm install @babel/plugin-proposal-decorators
- 在 package.json 文件中退出配置
"babel": {
"plugins": [
[
"@babel/plugin-proposal-decorators",
{"legacy": true}
]
]
}
启用装璜器语法反对(形式二)
npm install react-app-rewired customize-cra @babel/plugin-proposal-decorators
-
在我的项目根目录下创立 config-overrides.js 并退出配置
const {override, addDecoratorsLegacy} = require("customize-cra"); module.exports = override(addDecoratorsLegacy());
-
package.json
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test" }
2.2 解决 vscode 编辑器对于装璜器语法的正告
在 vscode 按 command + 逗号,而后在输入框中输出
javascript.implicitProjectConfig.experimentalDecorators
批改配置:"javascript.implicitProjectConfig.experimentalDecorators": true
3. Mobx + React
3.1 下载 Mobx
npm install mobx mobx-react
3.2 Mobx 工作流程
Action -> state -> Views
5. Mobx 数据监测
5.1 computed 计算值
什么时候应用计算值
将简单的业务逻辑从模板中进行抽离
5.2 autorun 办法
当监听的状态发生变化时,你想依据状态产生“成果”,请应用 autorun.
autorun 会在初始化的时候执行一次,会在每次状态发生变化时执行。