redux从入门到放弃

471 阅读4分钟

一. redux介绍

redux是基于JavaScript的状态管理工具。redux除了与react一起使用也可以单独进行使用。redux状态是由createStore函数进行处理,通过返回getState获取数据,dispatch触发action,subscribe触发订阅,通过接收reducer(reducer的概念是参考了JavaScript的reduce方法命名而来)进行状态的管理。

二. redux三大原则

  1. 单一数据源:所以数据保存在store的state中。
  2. state是只读的:唯一修改state的方式是触发action。
  3. 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"});