React状态管理库快速上手-Redux(一)

基本使用

安装

pnpm install @reduxjs/toolkit react-redux

创建一个仓库

定义state

createSlice相当于创建了一个模块仓库,initialState存放状态,reducers存放改变状态的方法。

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
	...
  }
})

export const { ... } = counterSlice.actions

export default counterSlice.reducer
useSelector在组件中取出state数据
const count = useSelector(state => state.counter.value) 
//useSelector等于如下-》
const selectCount = state => state.counter.value
const count = selectCount(store.getState())
在组件中使用数据
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
  decrement,
  increment,
  incrementByAmount,
  incrementAsync,
  selectCount
} from './counterSlice'
import styles from './Counter.module.css'

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



  return (
    <div>
      <div className={styles.row}>
        <button
          className={styles.button}
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          +
        </button>
        <span className={styles.value}>{count}</span>

        </button>
      </div>
      {/* 这里省略了额外的 render 代码 */}
    </div>
  )
}

定义reducer

Redux Toolkit 有一个名为 createSlice 的函数,它负责生成 action 类型字符串、action creator 函数和 action 对象的工作。我们要为这个 slice 定义一个名称,编写一个包含 reducer 函数的对象,它会自动生成相应的 action 代码。

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    increment: state => {
      // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。
      // 并不是真正的改变 state 因为它使用了 immer 库
      // 当 immer 检测到 "draft state" 改变时,会基于这些改变去创建一个新的
      // 不可变的 state
      state.value += 1
    },
    decrement: state => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    }
  }
})

export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer
createEntityAdapter 简化reducer操作

Redux Toolkit 包含 createEntityAdapter API,该 API 为具有归一化 state 的典型数据更新操作预先构建了 reducer。包括从 slice 中添加、更新和删除 items。createEntityAdapter 还会生成一些用于从 store 中读取值的记忆化 selectors。

import {
  createSlice,
  createSelector,
  createAsyncThunk,
  createEntityAdapter
} from '@reduxjs/toolkit'
import { client } from '../../api/client'
import { StatusFilters } from '../filters/filtersSlice'

const todosAdapter = createEntityAdapter()

const initialState = todosAdapter.getInitialState({
  status: 'idle'
})

// Thunk 函数
export const fetchTodos = createAsyncThunk('todos/fetchTodos', async () => {
  const response = await client.get('/fakeApi/todos')
  return response.todos
})

export const saveNewTodo = createAsyncThunk('todos/saveNewTodo', async text => {
  const initialTodo = { text }
  const response = await client.post('/fakeApi/todos', { todo: initialTodo })
  return response.todo
})

const todosSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    todoToggled(state, action) {
      const todoId = action.payload
      const todo = state.entities[todoId]
      todo.completed = !todo.completed
    },
    todoColorSelected: {
      reducer(state, action) {
        const { color, todoId } = action.payload
        state.entities[todoId].color = color
      },
      prepare(todoId, color) {
        return {
          payload: { todoId, color }
        }
      }
    },
    todoDeleted: todosAdapter.removeOne,
    allTodosCompleted(state, action) {
      Object.values(state.entities).forEach(todo => {
        todo.completed = true
      })
    },
    completedTodosCleared(state, action) {
      const completedIds = Object.values(state.entities)
        .filter(todo => todo.completed)
        .map(todo => todo.id)
      todosAdapter.removeMany(state, completedIds)
    }
  },
  extraReducers: builder => {
    builder
      .addCase(fetchTodos.pending, (state, action) => {
        state.status = 'loading'
      })
      .addCase(fetchTodos.fulfilled, (state, action) => {
        todosAdapter.setAll(state, action.payload)
        state.status = 'idle'
      })
      .addCase(saveNewTodo.fulfilled, todosAdapter.addOne)
  }
})

export const {
  allTodosCompleted,
  completedTodosCleared,
  todoAdded,
  todoColorSelected,
  todoDeleted,
  todoToggled
} = todosSlice.actions

export default todosSlice.reducer

export const { selectAll: selectTodos, selectById: selectTodoById } =
  todosAdapter.getSelectors(state => state.todos)

export const selectTodoIds = createSelector(
  // 首先,传递一个或多个 input selector 函数:
  selectTodos,
  // 然后,一个 output selector 接收所有输入结果作为参数
  // 并返回最终结果
  todos => todos.map(todo => todo.id)
)

export const selectFilteredTodos = createSelector(
  // 第一个 input selector:所有 todos
  selectTodos,
  // 第二个 input selector:所有 filter 值
  state => state.filters,
  // Output selector: 接收两个值
  (todos, filters) => {
    const { status, colors } = filters
    const showAllCompletions = status === StatusFilters.All
    if (showAllCompletions && colors.length === 0) {
      return todos
    }

    const completedStatus = status === StatusFilters.Completed
    // 根据 filter 条件返回未完成或已完成的 todos
    return todos.filter(todo => {
      const statusMatches =
        showAllCompletions || todo.completed === completedStatus
      const colorMatches = colors.length === 0 || colors.includes(todo.color)
      return statusMatches && colorMatches
    })
  }
)

export const selectFilteredTodoIds = createSelector(
  // 传入记忆化 selector
  selectFilteredTodos,
  // 并在 output selector 中导出数据
  filteredTodos => filteredTodos.map(todo => todo.id)
)

注册

Redux store 是使用 Redux Toolkit 中的 configureStore 函数创建的。configureStore 要求我们传入一个 reducer 参数。我们的应用程序可能由许多不同的特性组成,每个特性都可能有自己的 reducer 函数。当我们调用configureStore 时,我们可以传入一个对象中的所有不同的 reducer。
这里的注册类似于Vuex中的module

import { configureStore } from '@reduxjs/toolkit'
import usersReducer from '../features/users/usersSlice'
import postsReducer from '../features/posts/postsSlice'
import commentsReducer from '../features/comments/commentsSlice'

export default configureStore({
  reducer: {
    users: usersReducer,
    posts: postsReducer,
    comments: commentsReducer
  }
})

在index.js中

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './components/App'
import store from './store/configureStore'



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

if (process.env.NODE_ENV !== 'production' && module.hot) {
  module.hot.accept('./components/App', renderApp)
}

renderApp()

configureStore 做了什么?

  1. 将 todosReducer 和 filtersReducer 组合到根 reducer 函数中,它将处理看起来像 {todos, filters} 的根 state
  2. 使用根 reducer 创建了 Redux store
  3. 自动添加了 “thunk” middleware
  4. 自动添加更多 middleware 来检查常见错误,例如意外改变(mutate)state
  5. 自动设置 Redux DevTools 扩展连接
    configureStore相当于如下操作-》
import { combineReducers } from 'redux'

import todosReducer from './features/todos/todosSlice'
import filtersReducer from './features/filters/filtersSlice'

const rootReducer = combineReducers({
  // 定义一个名为 `todos` 的顶级 state 字段,值为 `todosReducer`
  todos: todosReducer,
  filters: filtersReducer
})

export default rootReducer
//index.js
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import rootReducer from './reducer'

const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware))

const store = createStore(rootReducer, composedEnhancer)
export default store

借用官方的图片

异步逻辑处理-Thunk

使用 thunk 需要在创建时将 redux-thunk middleware( Redux 的插件)添加到 Redux store 中。不过,Redux Toolkit 的 configureStore 函数已经自动为我们配置好了,所以我们可以继续在这里使用 thunk,然后使用createAsyncThunk创建Thunk,该函数接收两个参数:

  • 一个字符串,用作生成的 action types 的前缀
  • 一个 payload creator 回调函数,应该返回一个 Promise。这通常使用 async/await 语法编写,因为 async 函数会自动返回一个 Promise。
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'

// 省略 imports 和 state

export const fetchTodos = createAsyncThunk('todos/fetchTodos', async () => {
  const response = await client.get('/fakeApi/todos')
  return response.todos
})

const todosSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    // 省略 reducer cases
  },
  extraReducers: builder => {
    builder
      .addCase(fetchTodos.pending, (state, action) => {
        state.status = 'loading'
      })
      .addCase(fetchTodos.fulfilled, (state, action) => {
        const newEntities = {}
        action.payload.forEach(todo => {
          newEntities[todo.id] = todo
        })
        state.entities = newEntities
        state.status = 'idle'
      })
  }
})

// 省略 exports

createSlice 还接收一个叫 extraReducers 的选项,可以让同一个 slice reducer 监听其他 action types。这个字段应该是一个带有 builder 参数的回调函数,我们可以调用 builder.addCase(actionCreator, caseReducer) 来监听其他 actions。

所以,这里我们调用了 builder.addCase(fetchTodos.pending, (statea,action)=>{})。当该 action 被 dispatch 时,我们将运行设置 state.status = 'loading' reducer。我们可以对 fetchTodos.fulfilled 做同样的事情,并处理我们从 API 接收到的数据。

总结Redux

Redux使用如下:

  • 定义管理全局应用程序的 state
  • 在应用程序中编写用于描述“发生了什么”的 action 对象
  • 使用 reducer 函数,它会根据当前 state 和 action,创建并返回一个不可变的新 state
  • 使用 useSelector 读取 React 组件中的 Redux state
  • 使用 useDispatch 从 React 组件 dispatch actions
  • 异步函数逻辑需要创建Thunk

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/274569.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024.3.19

思维导图 模拟面试 1.友元的作用 答&#xff1a;通过关键字friend&#xff0c;可以让一些函数或者类&#xff0c;可以访问一个类中的私有数据成员。 2.匿名对象的作用 答&#xff1a;匿名对象就是没有名字的对象&#xff0c;是用来给有名对象进行初始化工作的。 3.常成员函…

使用Vscode连接云进行前端开发

使用Vscode连接云进行前端开发 1、ssh连接腾讯云 本人使用的是腾讯云。 然后vscode,用最新版&#xff0c;插件选择remote ssh&#xff0c;或者remote xxx下载过来。 然后点击远程资源管理器&#xff0c;选择SSH通道 然后输入命令如下。 ssh rootip然后输入密码 腾讯云应该…

Java使用itextpdf往pdf中插入图片

引入maven依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.9</version> </dependency>java代码 import cn.hutool.extra.qrcode.QrCodeUtil; import com.itextpdf.text.*; i…

nodejs 使用express插件multer文件上传,接收不到文件的bug

把路径改成绝对路径即可 改成 temp是你想上传到文件夹的路径&#xff0c;一般是在项目根目录下

未来汽车EE架构趋势

多种趋势推动着电动汽车&#xff08;EV&#xff09;、混合动力电动汽车&#xff08;HEV&#xff09;和插电式混合动力电动汽车&#xff08;PHEV&#xff09;的发展。城市化和可持续发展的压力促使各国政府出台支持性法规&#xff0c;使制造商从中受益&#xff0c;而税收优惠政策…

C#,图论与图算法,计算无向连通图中长度为n环的算法与源代码

1 无向连通图中长度为n环 给定一个无向连通图和一个数n,计算图中长度为n的环的总数。长度为n的循环仅表示该循环包含n个顶点和n条边。我们必须统计存在的所有这样的环。 为了解决这个问题,可以有效地使用DFS(深度优先搜索)。使用DFS,我们可以找到特定源(或起点)的长度…

湖北省地质灾害分布数据 崩塌滑坡泥石流空间分布地质灾害详查等数据集

地质灾害是指在自然或者人为因素的作用下形成的&#xff0c;对人类生命财产造成的损失、对环境造成破坏的地质作用或地质现象。地质灾害在时间和空间上的分布变化规律&#xff0c;既受制于自然环境&#xff0c;又与人类活动有关&#xff0c;往往是人类与自然界相互作用的结果。…

Spark-Scala语言实战(3)

在之前的文章中&#xff0c;我们学习了如何在来如何在IDEA离线和在线安装Scala&#xff0c;想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-Scala语言实…

Linux:Gitlab:16.9.2 创建用户及项目仓库基础操作(2)

我在上一章介绍了基本的搭建以及邮箱配置 Linux&#xff1a;Gitlab:16.9.2 (rpm包) 部署及基础操作&#xff08;1&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/136821311?spm1001.2014.3001.5501 本章介绍一下用户的创建&#xff0c;组内设置用户&…

xAI开发的一款巨大型语言模型(HLM)--Grok 1

在xAI发布Grok的权重和架构之后&#xff0c;很明显大型语言模型&#xff08;LLM&#xff09;的时代已经过去&#xff0c;现在是巨大型语言模型&#xff08;HLM&#xff09;的时代。这个混合专家模型发布了3140亿个参数&#xff0c;并且在Apache 2.0许可下发布。这个模型没有针对…

力扣--回溯算法51.N皇后

思路分析&#xff1a; isValue函数用于判断当前位置是否可以放置皇后&#xff0c;通过检查当前列、左上方斜线和右上方斜线是否有皇后来确定。dfs函数采用深度优先搜索的方式&#xff0c;在每一行尝试放置皇后&#xff0c;如果当前位置合法&#xff0c;则递归继续尝试下一行&a…

Stable Diffusion WebUI 生成参数:高清修复/高分辨率修复(Hires.fix)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 大家好&#xff0c;我是水滴~~ 在本篇文章中&#xff0c;我们将深入探讨 Stable Diffusion WebUI 的一个引人注目的生成参数——高分辨率修复&#xff08;Hires.fix&#xff09;。我们将逐一…

web前端之不一样的下拉菜单、不选中第一个元素的样式效果、伪类排除第一个元素、符号选择器、hover、not、first、child

MENU 效果图htmlcssJShtmlcss 效果图 htmlcssJS html <nav><ul><li class"navli"><h4>HTML5</h4><ul class"ulson"><li class"lison">HTML5</li></ul></li><li class"na…

微信小程序项目实战遇到的问题

我们以学生成绩平台来作为例子。这是我们想得到的效果。 以下是完整代码&#xff1a; index.js // index.js Page({//页面的初始数据data: {hello: 欢迎进入微信小程序的编程世界,score: 80,userArray: [{name: 张三,score: [66, 77, 86, 70, 90]},{name: 李四,score: [88, 7…

使用ES检索PDF等文档的全栈方案之前端demo(end)

写在前面 通过之前的系列文章&#xff0c;整个ES搜索文件的流程与大的问题已经统统扫除了&#xff0c;既然是全栈流程&#xff0c;是不能缺少前端查询页面的&#xff0c;前端需简单实现一个用户输入查询关键词句&#xff0c;发起搜索&#xff0c;页面以表格形式展示查询的结果…

【2024.3.19练习】统计子矩阵

题目描述 题目分析 这道题一开始没有思路&#xff0c;使用蛮力枚举的方法时间复杂度为&#xff0c;显然超时。 参考题解后学会了化二维问题为一维问题&#xff0c;先使用的复杂度限制子矩阵的高度&#xff0c;再考虑列&#xff0c;这样就将子矩阵的和问题转变为了连续子序列的…

机器人离散化阻抗控制

机器人离散化阻抗控制是一种控制策略&#xff0c;它结合了阻抗控制的思想与离散化方法&#xff0c;以实现对机器人运动与外力之间动态关系的精细调节。这种控制方法旨在使机器人在与环境交互时能够表现出期望的阻抗特性&#xff0c;从而实现对接触力和位置的精确控制。 在离散…

【源码&教程】基于GAN的动漫头像生成系统

1.研究背景 我们都喜欢动漫角色&#xff0c;并试图创造我们的定制角色。然而&#xff0c;要掌握绘画技巧需要巨大的努力&#xff0c;之后我们首先有能力设计自己的角色。为了弥补这一差距&#xff0c;动画角色的自动生成提供了一个机会&#xff0c;在没有专业技能的情况下引入定…

Word为图表设置图注并在图表清单中自动生成

1如果需要自动插入题注&#xff0c;请不要自己为文件增加新的标题样式或删除自带的标题1样式 2章节大标题最好是标题1&#xff0c;2,3而不要设置标题一、二、三&#xff0c;否则图例在自动生成时会显示 图一 -1&#xff0c;调整起来会非常不方便 若实在要使用大写中文标题&…

解决由于历史原因解析tflite失败的问题

文章目录 0. 背景1. tflite 历史遗留问题2. schema3. flatbuffers 编译器3.1 安装 FlatBuffers 编译器3.2. 编译 FlatBuffers schema 文件3.3 使用生成的 Python 文件 4 问题未解决终极解决方案 写在最前面&#xff1a;解决方法是升级tensorflow版本&#xff0c;重新生成tflite…
最新文章