Skip to content
On this page

周边库

🔗@reduxjs/toolkit

bash
pnpm i @reduxjs/toolkit react-redux

初始化

创建 Redux Store

app/store.js

js
import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: {}
})

为 React 提供 Redux Store

main.js

js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

import store from './app/store' 
import { Provider } from 'react-redux' 

ReactDOM.render(
  <Provider store={store}> 
    <App />
  </Provider>, 
  document.getElementById('root')
)

创建 Redux State Slice

创建 slice 需要一个字符串名称来标识切片、一个初始 state 以及一个或多个定义了该如何更新 state 的 reducer 函数。slice 创建后 ,我们可以导出 slice 中生成的 Redux action creators 和 reducer 函数。

Redux 要求我们通过创建数据副本和更新数据副本,来实现不可变地写入所有状态更新。不过 Redux Toolkit createSlice 和 createReducer 在内部使用 Immer 允许我们编写“可变”的更新逻辑,变成正确的不可变更新。

features/counter/counterSlice.js

js
import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    increment: state => {
      // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
      // 并不是真正的改变状态值,因为它使用了 Immer 库
      // 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
      // 不可变的状态
      state.value += 1
    },
    decrement: state => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    }
  }
})
// 每个 case reducer 函数会生成对应的 Action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

将 Slice Reducers 添加到 Store 中

app/store.js

js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice' 

export default configureStore({
  reducer: {
    counter: counterReducer 
  }
})

在 React 组件中使用 Redux 状态和操作

js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux' 
import { decrement, increment } from './counterSlice' 


export function Counter() {
  const count = useSelector(state => state.counter.value) 
  const dispatch = useDispatch() 

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())} 
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())} 
        >
          Decrement
        </button>
      </div>
    </div>
  )
}

结合ts

app/store.ts

ts
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
  reducer: {
    posts: postsReducer,
    comments: commentsReducer,
    users: usersReducer
  }
})

// 从 store 本身推断出 `RootState` 和 `AppDispatch` 类型
export type RootState = ReturnType<typeof store.getState> 
// 推断出类型: { posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch 

定义 slice state 和 action 类型

ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'

// 为 slice state 定义一个类型
interface CounterState { 
  value: number 
} 

// 使用该类型定义初始 state
const initialState: CounterState = { 
  value: 0 
} 

export const counterSlice = createSlice({
  name: 'counter',
  // `createSlice` 将从 `initialState` 参数推断 state 类型
  initialState,
  reducers: {
    increment: state => {
      state.value += 1
    },
    decrement: state => {
      state.value -= 1
    },
    // 使用 PayloadAction 类型声明 `action.payload` 的内容
    incrementByAmount: (state, action: PayloadAction<number>) => { 
      state.value += action.payload
    }
  }
})

export const { increment, decrement, incrementByAmount } = counterSlice.actions
// 选择器等其他代码可以使用导入的 `RootState` 类型
export const selectCount = (state: RootState) => state.counter.value 

export default counterSlice.reducer

定义 Hooks 类型

TIP

尽管你可以将 RootStateAppDispatch 类型导入每个组件, 更好的方式是创建 useDispatchuseSelector 钩子的类型定义,以便在你的应用程序中使用:

由于这些是实际变量,而不是类型,因此将它们定义在单独的文件中很重要,例如 app/hooks.ts,而不是 store 设置文件

app/hooks.ts

ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'

// 在整个应用程序中使用,而不是简单的 `useDispatch` 和 `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

在组件中使用标注过类型的钩子

ts
import React from 'react'

import { useAppSelector, useAppDispatch } from 'app/hooks'

import { decrement, increment } from './counterSlice'

export function Counter() {
  // `state` 参数已正确推断为 `RootState` 类型
  const count = useAppSelector(state => state.counter.value) 
  const dispatch = useAppDispatch() 
}