一. redux介绍
redux是基于JavaScript的状态管理工具。redux除了与react一起使用也可以单独进行使用。redux状态是由createStore函数进行处理,通过返回getState获取数据,dispatch触发action,subscribe触发订阅,通过接收reducer(reducer的概念是参考了JavaScript的reduce方法命名而来)进行状态的管理。
二. redux三大原则
- 单一数据源:所以数据保存在store的state中。
- state是只读的:唯一修改state的方式是触发action。
- reducer处理数据如何变化,reducer必须是纯函数。
三. redux基本使用
import { createStore } from "redux";
const initState = {
count: 0
}
//定义reducer处理action数据
function reducer(state = initState, action) {
switch (action.type) {
case "add":
return {...state,...{count: state.count + action.num}};
case "sub":
return {...state,...{count: state.count - action.num}};
default:
return state;
}
}
const store = createStore(reducer);
const unsubscribe = store.subscribe(() => {console.log(store.getState())});
store.dispatch({type: "add", num: 5});
store.dispatch({type: "sub", num: 1});
unsubscribe();
四. 自定义createStore(不带中间件)
function createStore(reducer) {
let state;
const subscribeList = [];
const dispatch = function(action) {
state = reducer(state,action);
subscribeList.forEach((fn) => {
fn();
})
}
const subscribe = function(callback) {
subscribeList.push(callback);
return () => {
subscribeList.forEach((fn,index) => {
if(fn === callback) {
subscribeList.splice(index,1);
return;
}
})
}
}
const getState = function() {
return state;
}
dispatch({type: "@Redux/initState"});
return {
dispatch,
subscribe,
getState
}
}
五. redux在react中基本使用
import { Component } from "react";
import { createStore } from "redux";
const initState = {
count: 0
}
function reducer(state = initState, action) {
switch (action.type) {
case "add":
return {...state,...{count: state.count + action.num}};
case "sub":
return {...state,...{count: state.count - action.num}};
default:
return state;
}
}
const store = createStore(reducer);
class Add extends Component {
constructor(props) {
super(props);
this.state = {
count: store.getState().count
}
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.setState({
count: store.getState().count
})
})
}
add = (num) => {
store.dispatch({type: "add",num})
}
componentWillUnmount() {
this.unsubscribe && this.unsubscribe();
}
render() {
return (
<div>
<button onClick={() => {this.add(1)}}>Add1</button> <button onClick={() => {this.add(2)}}>Add2</button> state -- {this.state.count}
</div>
)
}
}
class Sub extends Component {
constructor(props) {
super(props);
this.state = {
count: store.getState().count
}
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.setState({
count: store.getState().count
})
})
}
sub = (num) => {
store.dispatch({type:"sub",num});
}
componentWillUnmount() {
this.unsubscribe && this.unsubscribe();
}
render() {
return (
<div>
<button onClick={() => {this.sub(1)}}>Sub1</button> <button onClick={() => {this.sub(2)}}>Sub2</button> state -- {this.state.count}
</div>
)
}
}
function App() {
return (
<div>
<Add></Add>
<Sub></Sub>
</div>
);
}
export default App;
六. react-redux
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'
class Counter extends Component {
render() {
const { value, value2, onIncreaseClick } = this.props;
return (
<div>
<span>{value}</span>
<br/>
<span>{value2}</span>
<button onClick={onIncreaseClick}>Increase</button>
</div>
)
}
}
Counter.propTypes = {
value: PropTypes.number.isRequired,
onIncreaseClick: PropTypes.func.isRequired
};
const increaseAction = { type: 'increase' };
function reducer(state = { count: 0 }, action) {
const count = state.count;
switch (action.type) {
case 'increase':
return { count: count + 1 };
default:
return state
}
}
const store = createStore(reducer);
function mapStateToProps(state) {
return {
value: state.count,
value2: state.count + 1
}
}
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => dispatch(increaseAction)
}
}
const App = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
七. 自定义connect
import { Component, createContext } from "react";
import { createStore } from "redux";
const StoreContext = createContext();
const initState = {
count: 0
}
function reducer(state = initState, action) {
switch (action.type) {
case "add":
return { ...state, ...{ count: state.count + action.num } };
case "sub":
return { ...state, ...{ count: state.count - action.num } };
default:
return state;
}
}
const store = createStore(reducer);
class Add extends Component {
constructor(props) {
super(props);
}
add = (num) => {
this.props.add(num);
}
sub = (num) => {
this.props.sub(num);
}
render() {
return (
<div>
<button onClick={() => { this.add(1) }}>Add1</button> <button onClick={() => { this.add(2) }}>Add2</button> state -- {this.props.count}
<br />
<button onClick={() => { this.sub(1) }}>Sub1</button> <button onClick={() => { this.sub(2) }}>Sub2</button> state -- {this.props.count}
</div>
)
}
}
function connect(mapStateToProps, mapDispatchToProps) {
return function (WrapComponent) {
class HocConnect extends Component {
constructor(props, context) {
super(props);
}
componentDidMount() {
this.unsubscribe = this.context.subscribe(() => {
this.forceUpdate();
})
}
componentWillUnmount() {
this.unsubscribe && this.unsubscribe();
}
render() {
return (
<WrapComponent {...this.props} {...mapStateToProps(this.context.getState())} {...mapDispatchToProps(this.context.dispatch)} ></WrapComponent>
)
}
}
HocConnect.contextType = StoreContext;
return HocConnect;
}
}
const Add2 = connect(mapStateToProps, mapDispatchToProps)(Add);
function mapStateToProps(state) {
return {
count: state.count
}
}
function mapDispatchToProps(dispatch) {
return {
add: (num) => {
dispatch({ type: "add", num })
},
sub: (num) => {
dispatch({ type: "sub", num })
}
}
}
function App() {
return (
<div>
<StoreContext.Provider value={store}>
<Add2></Add2>
</StoreContext.Provider>
</div>
);
}
export default App;
八. redux-thunk
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { applyMiddleware, createStore } from 'redux';
import { Provider, connect } from 'react-redux';
import thunk from "redux-thunk";
// 中间件: dispatch一个action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware。
class Counter extends Component {
render() {
const { value, onIncreaseClick, onIncreaseClickAsync } = this.props;
return (
<div>
<span>{value}</span>
同步: <button onClick={onIncreaseClick}>Increase</button>
异步: <button onClick={onIncreaseClickAsync}>onIncreaseClickAsync</button>
</div>
)
}
}
const increaseAction = { type: 'increase' };
function reducer(state = { count: 0 }, action) {
const count = state.count;
switch (action.type) {
case 'increase':
return { count: count + 1 };
default:
return state
}
}
const store = createStore(reducer, applyMiddleware(thunk));
function mapStateToProps(state) {
return {
value: state.count
}
}
function onIncreaseClickAsync() {
return (dispatch) => {
setTimeout(() => {
dispatch(increaseAction);
}, 2000);
}
}
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => {
// 同步发送数据,直接传入一个action对象。
dispatch(increaseAction)
},
onIncreaseClickAsync: () => {
// 异步发送数据,传入一个函数,函数的参数接收dispatch
dispatch(onIncreaseClickAsync())
}
}
}
const App = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
九. 中间件原理
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
const store = createStore(reducer);
let dispatch = store.dispatch; // 原始的dispatch
const midAPi = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args),
};
const middlewaresChain = middlewares.map((middleware) =>
middleware(midAPi)
);
dispatch = compose(...middlewaresChain)(store.dispatch);
return {
...store,
dispatch,
};
};
}
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
十. 自定义createStore(带有中间件)
export default function createStore(reducer, enhancer) {
if (enhancer) {
return enhancer(createStore)(reducer);
}
let currentState;
let currentListeners = [];
function getState() {
return currentState;
}
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach((listener) => listener());
}
function subscribe(listener) {
currentListeners.push(listener);
return () => {
const index = currentListeners.indexOf(listener);
currentListeners.splice(index, 1);
};
}
dispatch({ type: "@REDUX/init" });
return {
getState,
dispatch,
subscribe,
};
}
十一. 自定义中间件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import applyMiddleware from './applyMiddleware';
import createStore from "./createStore";
import { Provider, connect } from 'react-redux';
import thunk from "redux-thunk";
class Counter extends Component {
render() {
const { value, onIncreaseClick, onIncreaseClickAsync } = this.props;
return (
<div>
<span>{value}</span>
同步: <button onClick={onIncreaseClick}>Increase</button>
异步: <button onClick={onIncreaseClickAsync}>onIncreaseClickAsync</button>
</div>
)
}
}
function logger(middle) {
const { getState, dispatch } = middle;
return (next) => {
return (action) => {
console.log('start')
console.log('after');
return dispatch;
}
}
}
const increaseAction = { type: 'increase' };
function reducer(state = { count: 0 }, action) {
const count = state.count;
switch (action.type) {
case 'increase':
return { count: count + 1 };
default:
return state
}
}
const store = createStore(reducer, applyMiddleware(thunk, logger));
function mapStateToProps(state) {
return {
value: state.count
}
}
function onIncreaseClickAsync() {
return (dispatch) => {
setTimeout(() => {
dispatch(increaseAction);
}, 2000);
}
}
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => {
dispatch(increaseAction)
},
onIncreaseClickAsync: () => {
dispatch(onIncreaseClickAsync())
}
}
}
const App = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
十二. redux多个reducer封装
import { createStore } from "redux";
import { combineReducers } from "redux";
function A(state = 0, action) {
switch (action.type) {
case "add":
return state + 1;
default:
return state;
}
}
function B(state = {name:"jack"}, action) {
switch (action.type) {
case "jian":
return {name:'jian'}
default:
return state;
}
}
const reducers = combineReducers({A,B}); //A和B两个reducer各自维护自己的状态,把返回的状态放到自己的键中。
const store = createStore(reducers);
store.dispatch({type:"add"});
console.log(store.getState()); //{A:1,B:"jack"}
store.dispatch({type:"jian"});
console.log(store.getState()); //{A:1,B:"jian"}
十三. 多个reducer本质
import { createStore } from "redux";
function reducerA(state,action) {
switch (action.type) {
case "add":
return state.reducerA + 1;
default:
return state.reducerA;
}
}
function reducerB(state,action) {
switch (action.type) {
case "jian":
return state.reducerB - 1
default:
return state.reducerB;
}
}
function reducer(state = {},action) {
return {
reducerA: reducerA(state,action),
reducerB: reducerB(state,action)
}
}
const store = createStore(reducer,{reducerA:0,reducerB:0});
store.subscribe(() => {
console.log(store.getState());
})
store.dispatch({type:"add"});
store.dispatch({type:"jian"});