redux,react-redux及其实现源码

93 阅读2分钟

使用方法

  1. createStore的使用
import {createStore} from 'redux'
function countReducer(state = 0, action){
    switch(action.type){
        case 'ADD':
            return state + 1
        case 'MINUS':
            return state - 1
        default:
            return state
    }
}
const store = createStore(countReducer)
export store
  1. subscribe和dispatch的使用
export default class reduxPage extends Component{
    componentDidMount(){
        this.unsubcribe = store.subscribe(()=>{
            this.forceUpdate()
        })
    }
    componentWillUnmount(){
        this.unsubscribe()
    }
    add=()=>{
        store.dispath({type: 'ADD'})
    }
    render(){
        return(
            <div>
                <p>{store.getState()}</p>
                <button onclick = {this.add}>add</button>
            </div>
        )
    }
}

createStore源码实现

export default function 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:'ADADADD/REDUX'})
    return {
        getState,
        dispatch,
        subscribe
    }
}

Redux 支持异步(中间键)

也就是支持dispatch函数的形式

// 以上例子增加异步的情况
minus=()=>{
    store.dispath((dispatch)=>{
        setTimeout(()=>{
            dispatch({type: 'MINUS'})
        },1000)
    })
}

前置知识compose

function f1(arg){
    console.log('f1',arg)
    return arg
}
function f2(arg){
    console.log('f2',arg)
    return arg
}
function f3(arg){
    console.log('f3',arg)
    return arg
}
//上一个函数的返回值是下一个函数的参数
const res = f1(f2(f3("omg")))
// compose的写法
const res = compose(f1,f2,f3)("omg")
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变成如下引用方式

import {createStorem, applyMiddleware} from 'redux'
import thunk from "redux-thunk"
import logger from "redux-logger"
function countReducer(state = 0, action){
    switch(action.type){
        case 'ADD':
            return state + 1
        case 'MINUS':
            return state - 1
        default:
            return state
    }
}
const store = createStore(countReducer,applyMiddleware(thunk,logger))
export store
// createStore增强
export default function createStore(reducer,enhancer){
    if(enhancer){
        return enhancer(createStore)(reducer)
    }
    ...
}
export default function applyMiddleware(...middlewares){
    return (createStore)=>(reducer)=>{
        const store = createStore(reducer)
        let dispatch = store.dispatch
        
        const midAPI = {
            getState: store.getState,
            dispatch: (action,...args)=>dispatch(action,...args)
        }
        const middlewareChain = middlewares.map((middleware)=>middleware(midAPI))
        const dispatch = compose(...middlewareChain)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }
}

中间键logger的实现

function logger({getState, dispatch}){
    return (next)=>(action)=>{
        console.log('=========')
        console.log("执行了"+action.type)
        const prevState = getState()
        console.log("prev state",prevState)
        const returnValue = next(action)
        const nextState = getState()
        console.log("next State", nextState)
        console.log('=========')
        return returnValue
    }
}

中间键thunk的实现

function thunk({getState, dispatch}){
    return (next)=>(action)=>{
        if(typeof action === "function"){
            return action(dispatch, getState)
        }
        return next(action)
    }
}

实现combineReducers

多个reducer的情况

const store = createStore(
    combineReducers({
        count: countReducer,
        user: userReducer
    })
)

使用的时候也通过键名来引用,store.getState().count或者store.getState().user

// 源码实现
export default function combineReducers(reducers){
    return function combination(state = {}, action){
        let nextState = {}
        let hasChanges = false
        for(const key in reducers){
            const reducer = reducers[key]
            nextState[key] = reducer(state[key], action)
            hasChanged = hasChanged || nextState[key] !== state[key]
        }
        hasChanged = hasChanged || Object.keys(nextState).length!==Object.keys(state).length
        return hasChanged ? nextState : state
    }
}

react redux

Provider

// 引用
ReactDOM.render(
    <Provider store = {store}>
        <App>
    </Provider>,
    document.getElementById("root")
)
// 实现
// 1 创建context对象
const Context = React.createContext()
// 2 Provider 传递Value
export function Provider({store,children}){
    return <Context.Provider value ={store}>{children}</Context.Provider>
}

connect

// 3 后代消费Provider传递下来的value
export const connect = (mapStateToProps,mapDispatchToProps)=>WrappedComponent=>props=>{
    const store = useContext(Context)
    const {getState, dispatch, subscribe} = store
    const stateProps = mapStateToProps(getState())
    const dispatchProps = {dispatch}

    const forceUpdate = useForeceUpdate()
    useLayoutEffect(()=>{
        const unsubscribe = subscribe(()=>{
            forceUpdate()
        })
        return ()=>{
            unsubscribe()
        }
    },[subscribe])
    return <WrappedComponent {...props} {...stateProps} {...dispatchProps} />
}
function useForceUpdate(){
    const [state, setState] = useState(0)
    const update = useCallback(()=>{
        setState(prev=>prev+1)
    },[])
    return update
}

react-redux的hook api

  1. 使用方式
import {useSelector, useDispatch} from 'react-redux'
export default function ReactRduxHookPage(props){
    const count = useSelector(({count}=>count))
    const dispatch = useDispatch()
    // 这里如果通过参数进行传递,还可以加useCallback
    const add = useCallback(()=>{
        ()=>{
            dispatch({type: "ADD"})
        }
    },[])
    <!-- const add = ()=>{
        dispatch({type: "ADD"})
    } -->
    return (
        <div>
            <button onClick = {add} >{count}</button>
        </div>
    )
}
  1. 源码实现
export function useSelector(selector){
    const store = useContext(Context)
    const {getState, subscribe} = store
    const selectState = selector(getState())
    useLayoutEffect(()=>{
        const unSubscribe = subscribe(()=>{
            forceUpdate
        })
        return ()=>{
            unsubscribe()
        }
    },[subscribe])
    return selectState
}
export function useDispatch(){
    const store = useContext(Context)
    const {dispatch} = store
    return dispatch
}