Skip to content

redux-toolkit

ReduxToolkit 是官方推荐的编写 Redux 逻辑的方法。

安装

安装@reduxjs/toolkit

npm install @reduxjs/toolkit react-redux

or 

yarn add @reduxjs/toolkit react-redux

安装react-redux

yarn add react-redux

基本用法

store/feature/counter.js

jsx
import {createSlice} from "@reduxjs/toolkit";

const counterSlice = createSlice({
    name: "counter",
    initialState: {
        count: 100
    },
    reducers: {
        addNumber: (state, {payload}) => {
            state.count += payload
        },
        subNumber: (state, {payload}) => {
            state.count -= payload
        }
    }
})

export const {addNumber, subNumber} = counterSlice.actions

export default counterSlice.reducer

store/index.js

jsx
import {configureStore} from "@reduxjs/toolkit";
import CounterReducer from "./feature/counter"

const store = configureStore({
    reducer: {
        counter: CounterReducer
    }
})

export default store

到这里store已经初始化完成了,接下来我们使用react-redux来读取数据

应用入口文件

jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {Provider} from "react-redux";
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <Provider store={store}>
            <App/>
        </Provider>
    </React.StrictMode>
);

页面中使用

jsx
import React, {PureComponent} from 'react';
import {connect} from "react-redux";
import {addNumber} from "../store/feature/counter";

class Home extends PureComponent {
    render() {
        return (
            <div>
                <h2>
                    Home counter: {this.props.count}
                </h2>
                <div>
                    <button onClick={() => {
                        this.props.addNumber(1)
                    }}>+1
                    </button>

                    <button onClick={() => {
                        this.props.addNumber(5)
                    }}>+5
                    </button>
                </div>
            </div>
        );
    }
}

const mapStateToProps = (state) => ({
    count: state.counter.count
})

const mapDispatchToProps = (dispatch) => ({
    addNumber(count) {
        dispatch(addNumber(count))
    },
    // 两种写法都可以
    // addNumber: (count) => dispatch(addNumber(count))  
})

export default connect(mapStateToProps, mapDispatchToProps)(Home);

redux-toolkit中的异步操作

最新版本已经舍去了第一种方式,这里用的是builder的方式

代码示例:

store/feature/home.js

js
import {createAsyncThunk, createSlice} from "@reduxjs/toolkit";

// 模拟网络请求
function fetchDemo() {
    return new Promise((resolve, reject) => {
        const data = [
            {
                id: 1,
                title: " title1"
            },
            {
                id: 2,
                title: " title2"
            },
            {
                id: 3,
                title: " title3"
            }
        ]
        setTimeout(() => {
            resolve(data)
        }, 3000)
    })
}

// 使用createAsyncThunk创建异步函数
export const fetchHomeMultidataAction = createAsyncThunk("fetch/homemultidata", async () => {
    const res = await fetchDemo()
    return res
})

const homeSlice = createSlice({
    name: "home",
    initialState: {
        banner: []
    },
    reducers: {
        changeBanner: (state, {payload}) => {
            state.banner = payload
        }
    },
    // 已经移除
    // extraReducers: {
    //     [fetchHomeMultidataAction.pending](state, action) {
    //         console.log("fetchHomeMultidataAction pending", action)
    //     },
    //     [fetchHomeMultidataAction.rejected](state, action) {
    //         console.log("fetchHomeMultidataAction rejected", action)
    //     },
    //     [fetchHomeMultidataAction.fulfilled](state, {payload}) {
    //         console.log("fetchHomeMultidataAction fulfilled", payload)
    //         state.banner = payload
    //     }
    // }
    extraReducers: (builder) => {
        builder.addCase(fetchHomeMultidataAction.pending, state => {
            console.log("fetchHomeMultidataAction pending", state)
        }).addCase(fetchHomeMultidataAction.fulfilled, (state, {payload}) => {
            console.log("fetchHomeMultidataAction fulfilled", payload)
            // 对banner数据进行赋值
            state.banner = payload
        })
    }
})

export const {changeBanner} = homeSlice.actions

export default homeSlice.reducer

store/index.js

js
import {configureStore} from "@reduxjs/toolkit";
import HomeReducer from "./feature/home"

const store = configureStore({
    reducer: {
        home: HomeReducer
    }
})

export default store

借助react-reduxconnect函数进行使用

pages/Profile.jsx

js
import React, {Component} from 'react';
import {fetchHomeMultidataAction} from "../store/feature/home";
import {connect} from "react-redux";

class Profile extends Component {
    componentDidMount() {
        this.props.fetchMultiData();
    }

    render() {
        const {banner} = this.props

        const list = banner ? (
            banner.map(item => {
                return (
                    <li key={item.id}>{item.title}</li>
                )
            })
        ) : null


        return (
            <div>
                <h2>
                    Profile
                </h2>
                <div>
                    home banner :
                    {list}
                </div>
            </div>
        );
    }
}


const mapStateToProps = (state) => ({
    banner: state.home.banner
})

const mapDispatchToProps = (dispatch) => ({
    fetchMultiData() {
        dispatch(fetchHomeMultidataAction())
    }
})

export default connect(mapStateToProps, mapDispatchToProps)(Profile);

除了使用builder进行赋值外,还可以直接在createAsyncThunk中进行赋值

pages/Profile.jsx

js
import React, {Component} from 'react';
import {fetchHomeMultidataAction} from "../store/feature/home";
import {connect} from "react-redux";

class Profile extends Component {
    componentDidMount() {
        this.props.fetchMultiData();
    }

    render() {
        ...
    }
}


const mapStateToProps = (state) => ({
    banner: state.home.banner
})

const mapDispatchToProps = (dispatch) => ({
    fetchMultiData() {
        // fetchHomeMultidataAction中支持传递额外参数
        dispatch(fetchHomeMultidataAction({page: 1}))
    }
})

export default connect(mapStateToProps, mapDispatchToProps)(Profile);

store/feature/home.js

js
import {createAsyncThunk, createSlice} from "@reduxjs/toolkit";

function fetchDemo() {
    return new Promise((resolve, reject) => {
        const data = [
            {
                id: 1,
                title: " title1"
            },
            {
                id: 2,
                title: " title2"
            },
            {
                id: 3,
                title: " title3"
            }
        ]
        setTimeout(() => {
            resolve(data)
        }, 3000)
    })
}

export const fetchHomeMultidataAction = createAsyncThunk("fetch/homemultidata", async (extraInfo, {
    dispatch,
    getState
}) => {
    const res = await fetchDemo()
    // 调用时不传extraInfo则为undefined
    console.log("extraInfo",extraInfo) // { page: 1 }
    console.log("getstate",getState())
    // 可以在这里直接调用dispatch赋值
    dispatch(changeBanner(res))
    return res
})

const homeSlice = createSlice({
    name: "home",
    initialState: {
        banner: []
    },
    reducers: {
        changeBanner: (state, {payload}) => {
            state.banner = payload
        }
    }
})

export const {changeBanner} = homeSlice.actions

export default homeSlice.reducer

redux-toolkit的数据不可变性

react开发过程中,我们总是会考虑到数据不可变性(直接赋值对象引用导致带来的问题),而redux-tooltik中很多场景是直接对源数据惊醒修改,也不符合纯函数的要求,但其实redux-toolkit满足了数据不可变性,它借助了一个第三方库,来保证数据不可变性。

类似的两个库:

immerJS

immutable-js

上次更新于: