【案例】使用React+redux实现一个Todomvc

About
大家好,我是且陶陶,今天跟大家分享一个redux的todoList案例,通过这个案例能够快速掌握redux的基本知识点🌹

❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…

前情回顾 - 什么是redux 🪷

最流行的状态管理工具之一。(类似于 vue中的vuex)

Redux和React是两个独立的工具/

三个核心概念🌟

  1. action(动作/行为):【对象格式】描述要做的事(例如:登陆、退出、增删改查等等…)
  2. reducer(函数):【函数格式 function reducer(state = 0,action){ } 】更新状态
  3. store(仓库):整合action(动作)和reduce(函数)
    在这里插入图片描述

store分配要做的事actionreducer

🍬TodoMVC案例

代码地址🍻:
TodoMvc
欢迎大家批评指正~

功能介绍 🌺

🍦 添加事项
🍦 删除事项
🍦 完成or未完成事项
🍦 全选反选
🍦 清空

在这里插入图片描述

🍿 静态结构

在这里插入图片描述

🍰 状态管理 - redux

一、创建store📂

在这里插入图片描述

  1. store/reducer/todos.js 中处理行为

    const initList = [{ id: 1, name: '学习日语,备考N1', isDone: true },{ id: 2, name: '学习英语,备考雅思', isDone: false },{ id: 3, name: '学习GO,找工作', isDone: false },
    ]
    export default function todosReducer(state = initList, sction) {return state
    }
    
  2. store/reducers/index.js 中合并单独的reducer并导出

    // 模块合并 并导出
    import todos from './todo'
    import { combineReducers } from 'redux'const rootReducer = combineReducers({ todos })
    export default rootReducer
  3. store/index.js中挂载 reducer和action

    // 创建仓库,挂载reducers 并导出
    import { createStore } from 'redux'
    import reducers from './reducers/index'
    // 创建store
    const store = createStore(reducers)
    export default store

二、引入redux🧊

index.jsx中,引入reduxreact-redux

用Provider包裹根组件,并提供store值

import ReactDOM from 'react-dom/client'
import App from './App'
import store from './store/index'
import { Provider } from 'react-redux'
import './styles/base.css'
import './styles/index.css'// 渲染UI界面
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<Provider store={store}><App></App></Provider>
)

三、使用仓库状态📉

  1. components/TodoMain.jsx 【列表内容组件】中,使用 useSelector, useDispatch 这两个hook 操作状态。

    import React from 'react'
    import TodoItem from './TodoItem'
    import { useSelector, useDispatch } from 'react-redux'
    export default function TodoMain() {// 拿到状态const todos = useSelector((state) => state.todos)**console.log(todos)**// 修改状态const dispatch = useDispatch()......
    

在这里插入图片描述

更改状态🍥

步骤

  1. 界面绑定onChange事件,dispatch触发行为。
  2. 定义一个action行为,声明actionType
  3. 根据行为在todosReducer中处理状态

功能实现🍹

界面渲染🕸️

渲染 事项📋

  1. TodoMain.jsx中。循环渲染todolist中的每一项。传递每一项item
 ......return (<section className="main"><input id="toggle-all" className="toggle-all" type="checkbox" /><label htmlFor="toggle-all">Mark all as complete</label><ul className="todo-list">{/* todolist的每一项 */}**{todos.map((item) => {return <TodoItem key={item.id} todos={item}></TodoItem>})}**</ul></section>)
  1. TodoItem.jsx子组件中接收每一项。并渲染

    1. 划线样式类名:completed
    2. 展示输入框类名:editing
    export default function TodoItem(**props**) {const todoitem = props.todosreturn (// completed - 划线,已完成事项// editing - 输入事项<li className={todoitem.done ? 'completed' : ''}><div className="view">{/* 复选框设置选中状态 */}<input className="toggle" type="checkbox" checked={todoitem.isDone} /><label>{todoitem.name}</label><button className="destroy"></button></div><input className="edit" /></li>)
    }

在这里插入图片描述

做到这里,我们会发现控制台报错:

在这里插入图片描述
意思是我们这里添加了checked属性,但是需要添加一个change事件。所以接下来需要添加change事件。

修改单项🐣

添加事件🐥

因为当前是受控组件,无法修改。所以需要给他一个onChange事件

onChange事件交给store去修改数据。


思路:

  1. 绑定onChange事件,在这个事件中用dispatch触发action行为
  2. 定义一个action行为
  3. 声明actionTypes
  4. 根据行为在todosReducer里面处理状态

代码:

  1. 绑定onChange事件

    1. 传递id和当前状态
    <inputclassName="toggle"type="checkbox"checked={todoitem.isDone}onChange={() => {dispatch(changeDone(todoitem.id, !todoitem.isDone))}}
    />
    
  2. 定义action行为

    import { CHANGE_STATE } from '../constants/todo'// 修改单个状态的行为
    export const changeDone = (id) => {return {type: CHANGE_STATE,id,}
    }
  3. 声明actionType

    // 声明 constantTypes
    export const CHANGE_STATE = 'todos/changeDone' // 修改单个复选框状态类型
  4. todosReducer里面处理状态

    case CHANGE_STATE:// 注意:状态不可变return state.map((item) => {if (item.id === action.id) {return {...item,isDone: action.isDone,}} else {return item}})
    
  5. 使用dispatch触发action

    import React from 'react'
    import { useDispatch } from 'react-redux'
    ...
    export default function TodoItem(props) {...const dispatch = useDispatch()return (...<inputclassName="toggle"type="checkbox"checked={todoitem.isDone}onChange={() => {**dispatch**(changeDone(todoitem.id, !todoitem.isDone))}}/>...)
    }

删除单项🐤

思路:

  1. 给X绑定点击事件 onClick
  2. 定义一个action行为
  3. 声明actionTypes
  4. 根据行为在todosReducer里面处理状态

代码:

  1. 给X绑定点击事件 onClick

    <button
    className="destroy"
    onClick={() => {dispatch(delTodo(todoitem.id))
    }}
    ></button>
    
  2. 定义一个action行为

    // 删除单个代办项
    export const delTodo = (id) => {return {type: DELETE_TODO,id,}
    }
  3. 声明actionTypes

    export const DELETE_TODO = 'todos/delTodo' // 删除单个待办
    
  4. 根据行为在todosReducer里面处理状态

      case DELETE_TODO:return state.filter((item) => {// 过滤掉与选择的这一行相同的idreturn item.id !== action.id})
    

    添加单项🦜

    1. 绑定onChange事件,得到输入框的输入内容

      import React, { useState } from 'react'
      import { useDispatch } from 'react-redux'
      import { addTodo } from '../store/actions/todo'export default function TodoHeader() {**const [inputValue, setInputValue] = useState('')// 添加单项todoconst addValue = (e) => {setInputValue(e.target.value)}**return (<header className="header"><h1>todos</h1><inputclassName="new-todo"placeholder="今天做什么?"value={inputValue}autoFocus**onChange={addValue}**/></header>)
      }
    2. 绑定onKeyDown 事件,键盘按下时传递输入项value

        <inputclassName="new-todo"placeholder="今天做什么?"value={inputValue}autoFocusonChange={addValue}onKeyDown={(e) => {if (e.key === 'Enter') {console.log('回车', inputValue)dispatch(addTodo(inputValue))setInputValue('') // 清空输入框}}}/>
      
    3. 定义一个action行为

      // 添加单个待办项
      export const addTodo = (inputValue) => {return {type: ADD_TODO,name: inputValue,}
      }
      
    4. 声明actionTypes

      export const ADD_TODO = 'todos/addTodo' // 添加单个待办项
      
    5. 根据行为在todosReducer里面处理状态

      case ADD_TODO:if (!action.name.trim()) return// 状态不可变!!!return [{id: state.length + 1,name: action.name,isDone: false,},...state,]
      

底部筛选🐩

在这里插入图片描述

<aside>
💡 要实现底部筛选,可以在footer中使用过滤器进行分发。</aside>

一、列表项绑定筛选后数据

  1. 声明actionTypes

    // 筛选栏标题
    export const SHOW_ALL = 'show_all'
    export const SHOW_COMPLETED = 'show_completed'
    export const SHOW_ACTIVE = 'show_active'
    // 筛选行为
    export const SET_VISIBILITY_FILTER = 'todos/setVisibilityFilter'
    
  2. 定义筛选栏标签的静态数据

    import { SHOW_ALL,SHOW_ACTIVE,SHOW_COMPLETED } from "./todo";export  const FILTER_TITLES = {[SHOW_ALL]: 'All',[SHOW_ACTIVE]: 'Active',[SHOW_COMPLETED]: 'Completed'}
    
  3. 定义一个action行为

    // 底部筛选栏 - 用于更新Redux store中的过滤状态
    export const setVisibilityFilter = (filter) => ({type: SET_VISIBILITY_FILTER,filter
    })
    
  4. 根据行为在todosReducer里面处理状态

    1. 新建一个reducer/filter.js
    import { SET_VISIBILITY_FILTER } from '../constants/todo'
    import { SHOW_ALL } from '../constants/todo'
    // 设置已完成&未完成,并返回参数。
    const visibilityFilter = (state = SHOW_ALL, action) => {switch (action.type) {case SET_VISIBILITY_FILTER:return action.filterdefault:return state}
    }export default visibilityFilter
    
    1. 新建一个selector/isVisible.js
    // todo项是否可见 方法
    import { SHOW_ACTIVE, SHOW_ALL, SHOW_COMPLETED } from '../constants/todo'export function selectVisible(state = [], filter) {switch (filter) {case SHOW_ALL:return statecase SHOW_ACTIVE:return state.filter((todo) => !todo.isDone)case SHOW_COMPLETED:return state.filter((todo) => todo.isDone)default:return state}
    }
    
  5. TodoMain.jsx中,使用筛选(未完成/已完成/全部)后的状态来循环渲染列表项

// 筛选出已完成or未完成or全部的项
// 传入两个参数-参数1:所有数据;参数2:过滤条件const visibleTodos = useSelector((state) =>selectVisible(state.todos, state.visibilityFilter))

二、底部筛选栏设置过滤条件

  1. TodoFooter.jsx中,循环渲染过滤条件。
  2. 给a链接绑定onClick事件,触发action行为。实现数据的过滤展示。
    <ul className="filters">{Object.keys(FILTER_TITLES).map((filterTitle) => (<li key={filterTitle}><ahref="./#"className={classNames({ selected: filterTitle === filter })}onClick={() => dispatch(setVisibilityFilter(filterTitle))}>{FILTER_TITLES[filterTitle]}</a></li>))}
    </ul>
    

删除全部已完成☘️

  1. 给按钮绑定点击事件 onClick

    <buttonclassName="clear-completed"onClick={() => dispatch(changeAll(true))}
    >Clear completed
    </button>
    
  2. 定义一个action行为

    // 清除所有已完成
    export const changeAll = (isDone) => {return {type: CHANGE_ALL,isDone,}
    }
    
  3. 声明actionTypes

    export const CHANGE_ALL = 'todos/changeAll' // 清除所有已完成
    
  4. 根据行为在todosReducer里面处理状态

    case CHANGE_ALL:return state.filter((item) => {return item.isDone !== action.isDone
    })
    

持久化存储 - 本地 🌈

在这里插入图片描述

  1. 定义一个action行为

    // 本地localstore存储
    export const setLocalToken = (todos) => ({type: SET_LOCAL_TOKEN,todos,
    })
    
  2. 声明actionTypes

    // 本地localstore存储
    export const SET_LOCAL_TOKEN = 'todos/setLocalToken'
    
  3. 根据行为在reducer里面处理状态

    case SET_LOCAL_TOKEN:return action.todos
    
  4. TodoMain.jsx中触发action

    const todos = useSelector((state) => state.todos)
    // 触发action,传入本地存储的状态useEffect(() => {const savedTodos = JSON.parse(localStorage.getItem('todos'))if (savedTodos) {dispatch(setLocalToken(savedTodos))}
    //[dispatch] 作为依赖数组。只有当 dispatch 更新时才重新执行 useEffect 中的逻辑}, [dispatch])
    // 状态存储到本地useEffect(() => {localStorage.setItem('todos', JSON.stringify(todos))}, [todos])
    

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Helm部署k8s应用

文章目录 一、概述1、什么是Helm2、特点3、工作流程4、核心概念 二、安装Helm1、二进制版本安装1.1、下载需要的版本1.2、解压1.3、将helm移动到指定路径1.4、验证 三、Helm安装资源顺序四、--set 的格式和限制1、最简单的name/value对2、多个name/value对3、更复杂的表达式4、…

Java语言程序设计基础篇_编程练习题*15.3 (移动小球)

*15.3 (移动小球) 编写一个程序&#xff0c;在面板上移动小球。应该定义一个面板类来显示小球&#xff0c;并提供向左、 向右 、向上和向下移动小球的方法&#xff0c;如图15-24c所示。请进行边界检査以防止球完全移到视线之外 代码展示&#xff1a;编程练习题15_3MoveBall.ja…

鸿蒙OpenHarmony Native API【drawing_pen.h】 头文件

drawing_pen.h Overview Related Modules: [Drawing] Description: 文件中定义了与画笔相关的功能函数 Since: 8 Version: 1.0 Summary Enumerations Enumeration NameDescription[OH_Drawing_PenLineCapStyle] { [LINE_FLAT_CAP], [LINE_SQUARE_CAP], [LINE_ROUND_…

Redis核心技术与实战学习笔记

Redis核心技术与实战学习笔记 最近想沉下心来看下redis&#xff0c;买了蒋德钧老师的《Redis 核心技术与实战》,这里记录一些学习笔记 希望能够坚持下去有想一起学习的童鞋&#xff0c;可以点击跳转到文章尾部获取学习资源,仅供学习不要用于任何商业用途!!! redis知识全景图 …

ROS2入门到精通—— 2-8 ROS2实战:机器人安全通过狭窄区域的方案

0 前言 室内机器人需要具备适应性和灵活性&#xff0c;以便在狭窄的空间中进行安全、高效的导航。本文提供一些让机器人在狭窄区域安全通过的思路&#xff0c;希望帮助读者根据实际开发适当调整和扩展 1 Voronoi图 Voronoi图&#xff1a;根据给定的一组“种子点”&#xff0…

Ubuntu22.04系统安装nodejs 14 保姆级教程

下载软件包 从NodeSource 的官方源下载并安装 Node.js 14.x 版本的软件包&#xff0c;适用于 Debian 和 Ubuntu 系统&#xff1a; curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - 更新软件源 更新软件源 sudo apt-get update 下载bodejs14 下载nodejs14 sud…

Jenkins卡在等待界面解决方法

一、问题 部署jenkins服务器出现Please wait while Jenkins is getting ready to work。 二、原因分析 jenkins里面文件指向国外的官网&#xff0c;因为防火墙的原因连不上。 三、解决方法 将配置文件里面的url换成国内镜像&#xff1a; &#xff08;1&#xff09;修改配…

WEB攻防-通用漏洞-SQL 读写注入-MYSQLMSSQLPostgreSQL

什么是高权限注入 高权限注入指的是攻击者通过SQL注入漏洞&#xff0c;利用具有高级权限的数据库账户&#xff08;如MYSQL的root用户、MSSQL的sa用户、PostgreSQL的dba用户&#xff09;执行恶意SQL语句。这些高级权限账户能够访问和修改数据库中的所有数据&#xff0c;甚至执行…

2024/7/23 英语每日一段

As malware has improved and evolved, it has pushed defense software to require constant connection and more extensive control. That deeper access also introduces a far higher possibility that security software—and updates to that software—will crash the …

RabbitMQ入门详解

前言 本篇文章将详细介绍rabbitmq的基本概念知识&#xff0c;以及rabbitmq各个工作模式在springboot中如何使用。 文章目录 介绍 简介 RabbitMQ 核心 生产者与消费者 Exchange Queue 工作模式 简单模式 工作队列模式 发布订阅模式 路由模式 主题模式 SpringBoot中…

java算法day21

java算法day21 77 组合216 组合总和Ⅲ17 电话号码的数字组合39 组合总和 这个阶段所要解决的问题都是有关回溯算法。 所以说解法基本上都是围绕回溯算法模板来完成。回溯算法可以这样总结&#xff0c;树形结构&#xff0c;横向遍历&#xff0c;纵向递归。递归出口收货结果并终…

(十九)原生js案例之h5地里位置信息与高德地图的初使用

h5 地里位置信息 1. 获取当前位置信息 window.onload function () {const oBtn document.querySelector("#btn");const oBox document.querySelector("#box");oBtn.onclick function () {window.navigator.geolocation.getCurrentPosition(function (…

c++----模版进阶

c----模版初阶&#xff1a;http://t.csdnimg.cn/PiYoD 一.非类型模版参数 模板参数除了可以是类型&#xff0c;还可以是常量。 例如&#xff1a; 这样就可以在类中使用这个n常量。 -------------------------------------------------------------------------------------…

鸿蒙OS物联网创新应用实训解决方案

摘要&#xff1a; 随着物联网技术的飞速发展&#xff0c;各种智能设备和传感器正在以前所未有的速度融入我们的日常生活。华为推出的鸿蒙操作系统&#xff08;HarmonyOS&#xff09;作为一款面向全场景、多设备、无缝连接的分布式操作系统&#xff0c;为物联网领域带来了全新的…

机器学习 | 回归算法原理——最小二乘法

Hi&#xff0c;大家好&#xff0c;我是半亩花海。很早便想学习并总结一本很喜欢的机器学习图书——立石贤吾的《白话机器学习的数学》&#xff0c;可谓通俗易懂&#xff0c;清晰形象。那就在此分享并作为学习笔记来记录我的学习过程吧&#xff01;本章的回归算法原理基于《基于…

【时序约束】读懂用好Timing_report

一、静态时序分析&#xff1a; 静态时序分析&#xff08;Static Timing Analysis&#xff09;简称 STA&#xff0c;采用穷尽的分析方法来提取出整个电路存在的所有时序路径&#xff0c;计算信号在这些路径上的传播延时&#xff0c;检查信号的建立和保持时间是否满足时序要求&a…

centos系统mysql主从复制(一主一从)

文章目录 mysql80主从复制&#xff08;一主一从&#xff09;一、环境二、服务器master1操作1.开启二进制日志2. 创建复制用户3. 服务器 slave1操作4. 在主数据库中添加数据 mysql80主从复制&#xff08;一主一从&#xff09; 一、环境 准备两台服务器&#xff0c;都进行以下操…

linux系统安装python3和pip

一、安装python 1、安装依赖环境 yum install gcc -y yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel yum install zlib zlib-devel openssl -y yum install openssl…

Qt源码交叉编译带openssl的Qt版本

一.背景 近期项目由于对接的后台服务是https的&#xff0c;之前交叉编译的Qt是不带openssl的&#xff0c;为了能支持https&#xff0c;必须要重新编译Qt。 二.环境 环境准备&#xff1a; Ubuntu版本 &#xff1a;18.04&#xff1b; openssl 版本&#xff1a;1.1.1.g&#xff1b…

vscode 搭建 golang 开发环境

介绍 在 vscode 搭建 go 的开发环境需要区分两个方向&#xff1a; go 1.19.0 及其更高版本go 1.19.0 之前的版本 为什么这么分&#xff0c;因为 vscode-go 插件自带的工具安装脚本全部都是装最新版的各类工具&#xff0c;这些工具中有部分要求 go 1.19.0 以上才能安装成功。…