react+typescript+wbpack5项目搭建(三)之Redux补充

文章介绍了如何在JavaScript应用中使用Redux进行状态管理,包括安装Redux及其React绑定库,创建ReduxStore,为React提供Store,创建Reducer(如navSlice),并在组件中使用useDispatch和useSelector钩子来访问和更新状态。
摘要由CSDN通过智能技术生成

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。

安装

npm install --save redux

多数情况下,还需要使用 React 绑定库和开发者工具。
Redux 默认并不包含 React 绑定库,需要单独安装。

npm install --save @reduxjs/toolkit react-redux
npm install --save-dev redux-devtools

重点
应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要编写 reducers。

1,创建 Redux Store

// src/redux/stor
import { configureStore } from '@reduxjs/toolkit'
import navSlice from './navSlice'

const store = configureStore({
  reducer: {
    nav: navSlice
  },
})

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

export default store

2,为 React 提供 Redux Store
React Redux 包含一个 组件,这使得 Redux store 能够在应用的其他地方使用:

import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'

import APP from './APP'

const container = document.getElementById('root') as HTMLElement
const root = ReactDOM.createRoot(container)
root.render(
  <Provider store={store}>
    <BrowserRouter>
      <APP />
    </BrowserRouter>
  </Provider>
)

3,创建 Redux State Slice

// src/redux/navSlice
import { createSlice } from '@reduxjs/toolkit'
import type { RootState } from './store'

// 定义初始化状态的类型
interface navState {
  value: number
}
// 初始化状态
const initialState: navState = {
  value: 33,
}

export const navSlice = createSlice({
  name: 'nav',
  initialState,
  reducers: {
    increment: (state) => {
      // Redux Toolkit 允许我们在 reducers 中编写 mutating 逻辑。
      // 它实际上并没有 mutate state 因为它使用了 Immer 库,
      // 它检测到草稿 state 的变化并产生一个全新的基于这些更改的不可变 state
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})

// 为每个 case reducer 函数生成 Action creators
export const { increment, decrement, incrementByAmount } = navSlice.actions
export const selectCount = (state: RootState) => state
export default navSlice.reducer

4,将 Slice Reducers 添加到 Store 中

// src/redux/store
import { configureStore } from '@reduxjs/toolkit'
import navSlice from './navSlice'

export default configureStore({
  reducer: {
    nav: navSlice
  },
})

5,组件中使用 Redux 状态和操作

import React, { useEffect, useState } from "react"
import { NavLink } from "react-router-dom"
import styles from './Nav.scss'
import { useDispatch, useSelector } from "react-redux"
import { RootState } from "../../redux/store"
import { increment } from "../../redux/navSlice"

interface Props {
  id: number;
  name: string;
  bool: boolean;
}

const Nav: React.FC<Props> = (props) => {

  const count = useSelector((state: RootState) => state.nav.value)
  const dispatch = useDispatch()

  const [perm, setPerm] = useState<boolean>(props.bool)

  const changeNav = () => {
    setPerm(!perm)
    dispatch(increment())
  }

  return (
    <div className={styles.nav}>
      <button onClick={() => { changeNav() }}>导航切换</button>{count}
      <span>{props.name}{props.id}</span>
      {
        perm ?
          <nav>
            <NavLink to="">首页</NavLink>
            <NavLink to="product/123">产品</NavLink>
            <NavLink to="contact">联系我们</NavLink>
            <NavLink to="messages">messages</NavLink>
          </nav> :
          <nav>
            <NavLink to="">首页</NavLink>
            <NavLink to="product/123">产品</NavLink>
          </nav>
      }
    </div>
  )
}


export default Nav

虽然你可以将 RootState 和 AppDispatch 类型导入每个组件, 更好的方式是创建 useDispatch 和 useSelector 钩子的类型定义,以便在你的应用程序中使用 有几个重要的原因:

  • 对于 useSelector ,不需要你每次输入(state: RootState)
  • 对于 useDispatch ,默认的 Dispatch 类型不知道 thunk 。为了正确调度 thunk ,你需要使用 store
    中包含 thunk 中间件类型的特定自定义 AppDispatch 类型,并将其与 useDispatch 一起使用。添加一个预先输入的useDispatch 钩子可以防止你忘记在需要的地方导入 AppDispatch。
// src/redux/hooks
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

由于这些是实际变量,而不是类型,因此将它们定义在单独的文件中很重要(hooks.tsx),而不是 store 设置文件。这允许你将它们导入到需要使用挂钩的任何组件文件中,并避免潜在的循环导入依赖问题。

最后修改组件中的使用方法

import React, { useEffect, useState } from "react"
import { NavLink } from "react-router-dom"
import styles from './Nav.scss'
// import { useDispatch, useSelector } from "react-redux"
import { useAppDispatch, useAppSelector } from "src/redux/hooks"
import { RootState } from "../../redux/store"
import { increment } from "../../redux/navSlice"

interface Props {
  id: number;
  name: string;
  bool: boolean;
}

const Nav: React.FC<Props> = (props) => {

  // const count = useSelector((state: RootState) => state.nav.value)
  // `state` 参数已正确推断为 `RootState` 类型
  const count = useAppSelector(state => state.nav.value)
  // const dispatch = useDispatch()
  const dispatch = useAppDispatch()

  const [perm, setPerm] = useState<boolean>(props.bool)

  const changeNav = () => {
    setPerm(!perm)
    dispatch(increment())
  }

  return (
    <div className={styles.nav}>
      <button onClick={() => { changeNav() }}>导航切换</button>{count}
      <span>{props.name}{props.id}</span>
      {
        perm ?
          <nav>
            <NavLink to="">首页</NavLink>
            <NavLink to="product/123">产品</NavLink>
            <NavLink to="contact">联系我们</NavLink>
            <NavLink to="messages">messages</NavLink>
          </nav> :
          <nav>
            <NavLink to="">首页</NavLink>
            <NavLink to="product/123">产品</NavLink>
          </nav>
      }
    </div>
  )
}


export default Nav
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值