react学习12:状态管理redux

useReducer

在学习 redux 之前,我们先学习下useReducer的使用。

前面用的 setState 都是直接修改值,那如果在修改值之前需要执行一些固定的逻辑呢?

这时候就要用 useReducer 了:

js 复制代码
import { Reducer, useReducer } from "react";

interface Data {
    result: number;
}

interface Action {
    type: 'add' | 'minus',
    num: number
}
function reducer(state: Data, action: Action) {

    switch(action.type) {
        case 'add':
            return {
                result: state.result + action.num
            }
        case 'minus': 
            return {
                result: state.result - action.num
            }
    }
    return state;
}

function App() {
  const [res, dispatch] = useReducer<Reducer<Data, Action>>(reducer, { result: 0});

  return (
    <div>
        <div onClick={() => dispatch({ type: 'add', num: 2 })}>加</div>
        <div onClick={() => dispatch({ type: 'minus', num: 1 })}>减</div>
        <div>{res.result}</div>
    </div>
  );
}

export default App;

useReducer 的类型参数传入 Reducer<数据的类型,action 的类型>

然后第一个参数是 reducer,第二个参数是初始数据。

点击加的时候,触发 add 的 action,点击减的时候,触发 minus 的 action。

当然,你直接 setState 也可以:

js 复制代码
import { useState } from "react";

function App() {
  const [res, setRes] = useState({ result: 0});

  return (
    <div>
        <div onClick={() => setRes({ result: res.result + 2 })}>加</div>
        <div onClick={() => setRes({ result: res.result - 1 })}>减</div>
        <div>{res.result}</div>
    </div>
  );
}

export default App;

有同学可能会说,用 useState 比 useReducer 简洁多了。

确实,这个例子不复杂,没必要用 useReducer。

但如果要执行比较复杂的逻辑呢?

用 useState 需要在每个地方都写一遍这个逻辑,而用 useReducer 则是把它封装到 reducer 里,通过 action 触发就好了。

当修改 state 的逻辑比较复杂,用 useReducer。

这就是useReducer的用法。

它有如下概念:

  • state 或 store
  • action
  • reducer
  • dispatch

了解了这些,学习redux就会轻松一些,因为useReducer就是抄了redux的逻辑写成的。

context + useReducer

我们使用contextuseReducer 来完成一个全局共享的状态库。

创建store.ts,定义全局对象

js 复制代码
import { nanoid } from 'nanoid'

export type TodoType = {
  id: string
  title: string
}

const initialState: TodoType[] = [
  {
    id: nanoid(5),
    title: '学习react',
  },
  {
    id: nanoid(5),
    title: '学习vue',
  },
]

export default initialState

创建reducer.ts,定义如何修改全局数据

js 复制代码
import { type TodoType } from './store'

export type ActionType = {
  type: string
  payload?: any // 附加的内容,要新增的todo等
}

function reducer(state: TodoType[], action: ActionType) {
  switch (action.type) {
    case 'add':
      return [...state, action.payload]
    case 'delete':
      return state.filter(todo => todo.id !== action.payload)
    default:
      throw new Error()
  }
}

export default reducer

创建index.tsx,通过执行useReducer函数,并传入reducer, initialState,返回数据state,及修改state的函数dispatch。然后把 state, dispatch 赋值给 context,这样所有的组件都能获取到state, dispatch。

js 复制代码
import { type FC, createContext, useReducer } from 'react'
import List from './List'
import InputForm from './InputForm'
import initialState from './store'
import reducer, { type ActionType } from './reducer'

export const TodoContext = createContext({
  state: initialState,
  dispatch: (action: ActionType) => {},
})

const Demo: FC = () => {
  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <TodoContext.Provider value={{ state, dispatch }}>
      <List />
      <InputForm />
    </TodoContext.Provider>
  )
}

export default Demo

创建List.tsx

js 复制代码
import { type FC, useContext } from 'react'
import { TodoContext } from '.'

const List: FC = () => {
  const { state, dispatch } = useContext(TodoContext)
  return (
    <ul>
      {state.map(item => (
        <li key={item.id}>
          <span>{item.title}</span>
          <button onClick={() => dispatch({ type: 'delete', payload: item.id })}>删除</button>
        </li>
      ))}
    </ul>
  )
}

export default List

创建InputForm.tsx

js 复制代码
import { type FC, type ChangeEvent, useState, useContext, memo } from 'react'
import { nanoid } from 'nanoid'
import { TodoContext } from '.'

const InputForm: FC = () => {
  const [text, setText] = useState('')
  const { dispatch } = useContext(TodoContext)
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setText(e.target.value)
  }
  const handleSubmit = (e: ChangeEvent<HTMLFormElement>) => {
    e.preventDefault()
    dispatch({ type: 'add', payload: { id: nanoid(5), title: text } })
  }
  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="new-todo">what do you want to do?</label>
      <br />
      <input
        type="text"
        id="new-todo"
        style={{ border: '1px solid #ccc' }}
        onChange={handleChange}
        value={text}
      />
      <button type="submit">添加</button>
    </form>
  )
}

export default InputForm

这样就完成了一个简化版本的redux

redux

下面我们看一个真实项目如何使用 redux

首先安装 @reduxjs/toolkitreact-redux:

js 复制代码
npm i  @reduxjs/toolkit react-redux -S

新建 index.ts

js 复制代码
import { configureStore } from '@reduxjs/toolkit'
import userReducer, { type UserStateType } from './userReducer'
import pageInfoReducer, { type PageInfoStateType } from './pageInfoReducer'


export type StateType = {
  user: UserStateType
  pageInfo: PageInfoStateType
}

export default configureStore({
  // reducer的值就是全局的state数据
  reducer: {
    user: userReducer,
    pageInfo: pageInfoReducer,
  },
})

新建userReducer.ts

js 复制代码
import { createSlice, type PayloadAction } from '@reduxjs/toolkit'

export interface UserStateType {
  username: string
  nickname: string
}

const initialState: UserStateType = {
  username: '',
  nickname: '',
}

export const userSlice = createSlice({
  name: 'user',
  initialState: initialState,
  reducers: {
    // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
    // 并不是真正的改变状态值,因为它使用了 Immer 库
    // 可以检测到"草稿状态" 的变化并且基于这些变化生产全新的
    // 不可变的状态
    logoutReducer: state => {
      state.username = ''
      state.nickname = ''
    },
    setUserInfoReducer: (state: UserStateType, action: PayloadAction<UserStateType>) => {
      state.username = action.payload.username
      state.nickname = action.payload.nickname
    },
  },
})

export const { logoutReducer, setUserInfoReducer } = userSlice.actions

export default userSlice.reducer

新建pageInfoReducer.ts

js 复制代码
import { createSlice, type PayloadAction } from '@reduxjs/toolkit'

export interface PageInfoStateType {
  title: string
  desc?: string
  js?: string
  css?: string
  isPublish?: boolean
}

const initialState: PageInfoStateType = {
  title: '',
  desc: '',
  js: '',
  css: '',
  isPublish: false,
}

export const pageInfoSlice = createSlice({
  name: 'pageInfo',
  initialState: initialState,
  reducers: {
    resetPageInfo: (state: PageInfoStateType, action: PayloadAction<PageInfoStateType>) => {
      Object.assign(state, action.payload)
    },

    changePageTitle: (state: PageInfoStateType, action: PayloadAction<string>) => {
      state.title = action.payload
    },
  },
})

export const { resetPageInfo, changePageTitle } = pageInfoSlice.actions

export default pageInfoSlice.reducer

上面的步骤定义了数据源,以及定义了修改数据源的方法。

那怎么在页面获取数据呢?

新建一个文件useGetUserInfo.ts:

js 复制代码
import { useSelector } from 'react-redux'
import { type StateType } from '../store'
import { type UserStateType } from '../store/userReducer'

function useGetUserInfo() {
  const { username, nickname } = useSelector<StateType, UserStateType>(state => state.user)
  return { username, nickname }
}

export default useGetUserInfo

StateType类型为整个数据源的类型

UserStateType类型为整个数据源里面属性user的类型

那如何更新数据呢?

js 复制代码
import { resetPageInfo } from '@/store/pageInfoReducer'
import { useDispatch } from 'react-redux'

const dispatch = useDispatch()
const handleValuesChange = () => {
    const values = form.getFieldsValue()
    dispatch(resetPageInfo(values))
}

redux 包括以下核心概念:

  • state 或 store:数据源
  • reducers:定义了如何修改数据源
  • dispatch:触发reducer,并传入新的值
相关推荐
AAA阿giao1 小时前
深入理解 JavaScript 中的 Symbol:独一无二的“魔法钥匙”
前端·javascript·ecmascript 6
晴栀ay1 小时前
JS面向对象:从"猫"的视角看JavaScript的OOP进化史
前端·javascript·面试
lichong9511 小时前
Android 弹出进度条对话框 避免用户点击界面交互
java·前端·javascript
ycgg1 小时前
别再只用 --xxx!CSS @property 解锁自定义属性的「高级玩法」
前端·css
灵犀坠1 小时前
前端知识体系全景:从跨域到性能优化的核心要点解析
前端·javascript·vue.js·性能优化·uni-app·vue
超哥的一天1 小时前
【前端】每天一个知识点-NPM
前端·node.js
海边的云1 小时前
vue对接海康摄像头-H5player
前端
小飞侠在吗1 小时前
vue 开发前的准备
前端·javascript·vue.js
redRain1 小时前
Next.js助你5分钟搭建AI聊天室
react.js·ai编程