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.mfbz.cn/a/470437.html

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

相关文章

MATLAB环境下基于改进最大相关峭度解卷积的滚动轴承故障诊断

相关峭度解卷积MCKD是一种新的解卷积方法&#xff0c;其设计了一个新的目标函数—相关峭度&#xff0c;并以此为优化目标设计一系列的FIR滤波器&#xff0c;为实现最好的效果&#xff0c;需要从中找到最优滤波器并最终实现对信号中噪声的抑制和对信号中冲击成分的突出的目的。M…

产品推荐 | 基于XC7K325T的FMC接口万兆光纤网络验证平台

01、产品概述 TES307是一款基于XC7K325T FPGA的万兆光纤网络验证平台&#xff0c;板卡具有1个FMC&#xff08;HPC&#xff09;接口&#xff0c;4路SFP万兆光纤接口、4路SATA接口、1路USB3.0接口。 板载高性能的FPGA处理器可以实现光纤协议、SATA总线控制器、以及USB3.0高速串…

长安链Docker Java智能合约引擎的架构、应用与规划

#功能发布 长安链3.0正式版发布了多个重点功能&#xff0c;包括共识算法切换、支持java智能合约引擎、支持后量子密码、web3生态兼容等。我们接下来为大家详细介绍新功能的设计、应用与规划。 在《2022年度长安链开源社区开发者调研报告》中&#xff0c;对Java合约语言支持是开…

华为配置HTTPS服务器实验

配置HTTPS服务器示例 组网图形 图1 配置HTTPS服务器组网图 组网需求配置思路配置注意事项操作步骤配置文件 组网需求 如图1所示&#xff0c;用户通过Web方式访问网关设备AP。 为了防止传输的数据不被窃听和篡改&#xff0c;实现对设备的安全管理&#xff0c;网络管理员要…

Makefile编译make complie时报错的心路历程

本次报错是在Makefile文件里面找错 Makefile文件找错的方法很复杂&#xff0c;必须要有一双慧眼&#xff0c;一层一层剥离分析 第一个错误&#xff08;还有一个错在全志 76节 23&#xff1a;35的时候连接wiringPi库&#xff09; 第二个错误&#xff1a; undefined reference…

Java:类和对象

目录 1.面对对象的初步认识1.1 什么是面向对象&#xff1f;&#xff08;Java当中一切皆为对象&#xff09;1.2 面对对象与面对过程 2.类的定义和使用2.1简单认识类2.2 类的定义格式 3.类的实例化3.1 什么是实例化3.2类和对象的说明 4.this引用4.1为什么要使用this引用4.2 什么是…

docker小白第十四天之Portainer与CIG

Portainer简介 Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 Portainer命令安装 # 一个容器可以同时起多个-p端口&#xff0c;restartalways表示随时在线&#xff0c;重启机器后也…

O2OA红头文件流转与O2OA版式公文编辑器基本使用

O2OA开发平台在流程管理中&#xff0c;提供了符合国家党政机关公文格式标准&#xff08;GB/T 9704—2012&#xff09;的公文编辑组件&#xff0c;可以让用户在包含公文管理的项目实施过程中&#xff0c;轻松地实现标准化公文格式的在线编辑、痕迹保留、手写签批等功能。并且可以…

[uni-app] uni.createAnimation动画在APP端无效问题记录

文章目录 uni.createAnimation动画描述动画代码templatejs部分 问题原因改进方案template js部分改动git 改进截图 uni.createAnimation 动画描述 实现一个以左上角为锚点,以Z轴做平面抬起及落下的动画效果 动画代码 template <image v-if"showHot(item.cname)&quo…

锂电极片生产中机器视觉系统的多元检测能力

随着新能源行业的快速发展&#xff0c;锂电池作为核心组件&#xff0c;其生产质量受到了前所未有的关注。在锂电极片的生产过程中&#xff0c;机器视觉系统以其高精度、高效率的特点&#xff0c;成为了保障产品质量的关键工具。本文将探讨机器视觉系统在锂电极片生产中可以检测…

WanAndroid(鸿蒙版)开发的第六篇

前言 DevEco Studio版本&#xff1a;4.0.0.600 WanAndroid的API链接&#xff1a;玩Android 开放API-玩Android - wanandroid.com 其他篇文章参考&#xff1a; 1、WanAndroid(鸿蒙版)开发的第一篇 2、WanAndroid(鸿蒙版)开发的第二篇 3、WanAndroid(鸿蒙版)开发的第三篇 …

GO语言:函数、方法、面向对象

本文分享函数的定义、特性、defer陷阱、异常处理、单元测试、基准测试等以及方法和接口相关内容 1 函数 函数的定义 func 函数名(参数列表) (返回值列表) { // 函数体&#xff08;实现函数功能的代码&#xff09; } 匿名函数的定义就是没有函数名&#xff0c;可以当做一个函…

第十九章 linux部署scrapyd

文章目录 1. linux部署python环境1. 部署python源文件环境2. 下载python3. 解压安装包4. 安装5. 配置环境变量6. 检查是否安装成功7. 准备python使用的包8. 安装scrapyd 1. linux部署python环境 1. 部署python源文件环境 yum install gcc patch libffi-devel python-devel zl…

【源码阅读】EVMⅢ

参考[link](https://blog.csdn.net/weixin_43563956/article/details/127725385 大致流程如下&#xff1a; 编写合约 > 生成abi > 解析abi得出指令集 > 指令通过opcode来映射成操作码集 > 生成一个operation 以太坊虚拟机的工作流程&#xff1a; 由solidity语言编…

WordPress站点如何实现发布文章即主动推送到神马搜索引擎?

平时boke112百科很少关注到神马搜索引擎&#xff0c;近日有站长留言想要实现WordPress站点发布文章就主动推送到神马搜索引擎&#xff0c;而且推送成功就自动添加一个自定义字段&#xff0c;以防重复推送。 登录进入神马站长平台后才知道神马也有一个API推送功能&#xff0c;不…

软件工程-第11章 内容总结

如果不想读这本书&#xff0c;直接看这一章即可。 11.1 关于软件过程范型 11.2 关于软件设计方法

SQLiteC/C++接口详细介绍sqlite3_stmt类(一)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类简介 下一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;二&#xff09; ​ 序言&#xff1a; 本文开始了SQLite的第二个类的详细介绍…

【技术栈】Spring Cache 简化 Redis 缓存使用

​ SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;保留赤子之心也许是种幸运吧 ​ 本文封面由 凯楠&#x1f4f8; 友情提供 目录 本栏传送门 1. Spring Cache 介绍 2. Spring Cache 常用注解 注&#xff1a;手机端浏览本文章…

TCP/IP ⽹络模型

TCP/IP ⽹络模型 对于同⼀台设备上的进程间通信&#xff0c;有很多种⽅式&#xff0c;⽐如有管道、消息队列、共享内存、信号等⽅式&#xff0c;⽽对于不同设备上的进程间通信&#xff0c;就需要⽹络通信&#xff0c;⽽设备是多样性的&#xff0c;所以要兼容多种多样的设备&am…

[PwnThyBytes 2019]Baby_SQL

[PwnThyBytes 2019]Baby_SQL 查看源码发现 下载源码&#xff0c;首先观察index.php 首先进入index.php&#xff0c;会执行session_start();启动session这里通过foreach将所有的环境变量的值都遍历了一遍&#xff0c;并且都使用了addslashes()进行转义&#xff0c;然后就定义了…