Visual LabINTERACTIVE LEARNING
框架原理第 5 / 7 章

Redux Toolkit 与 connect

进入现代 Redux 工具链,并回看 connect 的实现思路。

React组件Redux
进阶预计 58 分钟查看源文 ↗

Rtk redux toolkit

官方推荐的编写redux逻辑的标准方式

@reduxjs/toolkit,集成了redux-thunk等中间件

使用方式

创建store
//     

import homeReducer from "./reducer";
import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
    reducer: {
        home: homeReducer
    }
})
export default store
创建分片reducer

提示

异步组件还可以从createAsyncThunk返回结果,使用extraReducer的方式来 实现dispatch的派发,但是代码会繁琐一些

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

const initState = {
    count: 1
}

const countSlice = createSlice({
    name: 'home',
    initialState: initState,
    reducers: {
    //同步actions
        addNumber(state, { payload }) {
            state.count += payload
        },
        subNumber(state, { payload }) {
            state.count -= payload
        }
    }
})

//异步action
export const fetchData = createAsyncThunk('fetchData', async (params, store) => {
    const res = await new Promise(resolve => {
        resolve({
            data: {
                count: 3
            }
        })
    })
    store.dispatch(addNumber(res.data.count))
})
//导出reducer
export default countSlice.reducer
//导出同步action    
export const { addNumber, subNumber } = countSlice.actions
组件内使用
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addNumber, fetchData, subNumber } from '../store/reducer'
export class Home extends Component {
    render() {
        const { count, addCount, subCount, fetchCount } = this.props
        return (
            <div>
                <div>Home</div>
                <div> count: {count}</div>
                <button onClick={e => addCount(1)}>+</button>
                <button onClick={e => subCount(1)}>-</button>
                <button onClick={e => fetchCount(1)}>fetchCount</button>
            </div>
        )
    }
}

const mapStateToProps = (state) => ({ count: state.home.count })
const mapDispatchToProps = (dispatch) => ({
    addCount: (num) => dispatch(addNumber(num)),
    subCount: (num) => dispatch(subNumber(num)),
    fetchCount: (num) => dispatch(fetchData(num))
})

export default connect(mapStateToProps, mapDispatchToProps)(Home)

数据不可变原理

底层使用了immerjs,实现创建一个新的对象,但是不变的部分,仍然引用原来不改变的属性,实现复用和,减少内存浪费

实现自定义的connect

connect本身是一个高阶函数,返回一个高阶组件

//connect.js

import React, { PureComponent } from 'react'
import { storeContext } from './provider'

// 一个高阶函数,返回一个高阶组件
export function connect(mapStateToProps, mapActionsToProps) {
    return (WrapComponent) => {

        class NewComponent extends PureComponent {
            constructor(props, context) {
                super(props)
                this.state = mapStateToProps(context.getState())

            }
            componentDidMount() {
                this.unSubsribe = this.context.subscribe(() => {
                    this.setState(mapStateToProps(this.context.getState()))
                })
            }
            componentWillUnmount() {
                this.unSubsribe()
            }

            render() {
                const actions = mapActionsToProps(this.context.dispatch)
                const state = mapStateToProps(this.context.getState())
                return (
                    <WrapComponent {...this.props} {...state}{...actions} />
                )
            }
        }
        NewComponent.contextType = storeContext
        return NewComponent
    }
}

创建一个上下文用来共享整个store

import { createContext } from 'react'

export const storeContext = createContext()
export const Provider = storeContext.Provider