【案例】使用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.mzph.cn/news/875714.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序之计算器

在日常生活中&#xff0c;计算器是人们广泛使用的工具&#xff0c;可以帮助我们快速且方便地计算金额、成本、利润等。下面将会讲解如何开发一个“计算器”微信小程序。 一、开发思路 1、界面和功能 “计算器”微信小程序的页面效果如图所示 在计算器中可以进行整数和小数的…

C++学习笔记——模板

学习视频 文章目录 模板的概念函数模板函数模板语法函数模板注意事项函数模板案例普通函数与函数模板的区别普通函数与函数模板的调用规则模板的局限性 类模板类模板与函数模板区别类模板中成员函数创建时机类模板对象做函数参数类模板与继承类模板成员函数类外实现类模板分文件…

box-shadow属性的复合写法及高级用法,超详细!

前言&#xff1a;最近又叕看到了一个好看的特效&#xff0c;随后整理了一下&#xff0c;发现实现起来主要靠一个css属性就实现了&#xff0c;有一次刷新了我对css强大的认知&#x1f60e;&#xff0c;这个属性就是box-shadow&#xff0c;平常我们用到的比较少&#xff0c;但是针…

oracle中存储过程的写法

存储过程常规语法&#xff1a; 实际业务例子&#xff1a; CREATE OR REPLACE TRIGGER "TRI_B00_02_ONLY_GUID" BEFORE/AFTER INSERT OR UPDATE OR DELETE ON B00_02 FOR EACH ROW declare t_guid varchar2(300) : ; --GUID t_cnt int : 0; BEGIN t_guid : :NEW…

【深度学习】大模型GLM-4-9B Chat ,微调与部署(1)

下载好东西&#xff1a; 启动容器环境: docker run -it --gpus all --net host --shm-size8g -v /ssd/xiedong/glm-4-9b-xd:/ssd/xiedong/glm-4-9b-xd kevinchina/deeplearning:pytorch2.3.0-cuda12.1-cudnn8-devel-yolov8train bashpip install typer tiktoken numpy1.2…

ROS2入门到精通—— 2-11 ROS2实战:实现基于voronoi_planner的全局规划(一)!!!保姆级教程

实现基于voronoi_planner的全局规划将分为两篇博文进行讲解 本文参考该大佬代码: https://github.com/nkuwenjian/voronoi_planner.githttps://github.com/nkuwenjian/voronoi_layer.git将上面的ROS1代码移植到ROS2,移植不易,中间遇到很多坑 0 前言 针对一些狭窄区域,可能…

【机器学习】梯度下降的基本概念和如何使用梯度下降自动化优化w和b

引言 梯度下降是一种用于寻找函数最小值的优化算法&#xff0c;它在机器学习中广泛用于训练模型&#xff0c;如线性回归、神经网络等 一、梯度下降的基本概念 1.1 目标函数 在机器学习中&#xff0c;这通常是损失函数&#xff08;如均方误差、交叉熵等&#xff09;&#xff0…

深度学习趋同性的量化探索:以多模态学习与联合嵌入为例

深度学习趋同性的量化探索&#xff1a;以多模态学习与联合嵌入为例 参考文献 据说是2024年最好的人工智能论文&#xff0c;是否有划时代的意义&#xff1f; [2405.07987] The Platonic Representation Hypothesis (arxiv.org) ​arxiv.org/abs/2405.07987 趋同性的量化表达 …

Pytorch使用教学7-张量的广播

PyTorch中的张量具有和NumPy相同的广播特性&#xff0c;允许不同形状的张量之间进行计算。 广播的实质特性&#xff0c;其实是低维向量映射到高维之后&#xff0c;相同位置再进行相加。我们重点要学会的就是低维向量如何向高维向量进行映射。 相同形状的张量计算 虽然我们觉…

JAW:一款针对客户端JavaScript的图形化安全分析框架

关于JAW JAW是一款针对客户端JavaScript的图形化安全分析框架&#xff0c;该工具基于esprima解析器和EsTree SpiderMonkey Spec实现其功能&#xff0c;广大研究人员可以使用该工具分析Web应用程序和基于JavaScript的客户端程序的安全性。 工具特性 1、动态可扩展的框架&#x…

模拟ADG主库归档文件丢失,备库出现gap(增量备份解决)

文章目录 一、说明二、环境信息2.1.主备库环境信息2.2.检查主备是否同步正常 三、模拟日志断档3.1.模拟主库归档文件丢失3.2 查看主库状态出现GAP 四、RMAN增量备份恢复备库同步4.1 RMAN增量恢复备库4.2 开启备库redo同步4.3 主备库验证同步 一、说明 模拟Oracle主库归档文件丢…

Git基本原理讲解、常见命令、Git版本回退、Git抛弃本地分支拉取仓库最新分支

借此机会写篇博客汇总一下自己去公司实习之后遇到的一些常见关于Git的操作。 Git基本认识 Git把数据看作是对小型文件系统的一组快照&#xff0c;每次提交更新&#xff0c;或在Git中保存项目状态时&#xff0c;Git主要对当时的全部文件制作一个快照并保存这个快照的索引。同时…

【C++】选择结构案例-三只小猪称体重

案例问题 假设有三只小猪A、B、C&#xff0c;在输入三者体重后希望能输出他们各自的体重并测出谁最重 思路 先让A与B相比较&#xff0c;如果A重&#xff0c;则让A和C相比较&#xff0c;如果A重则输出A最重&#xff0c;否则输出C最重 在最开始的条件&#xff08;AB相比较&am…

浅谈Devops

1.什么是Devops DevopsDev&#xff08;Development&#xff09;Ops&#xff08;Operation&#xff09; DevOps&#xff08;Development和Operations的混合词&#xff09;是一种重视“软件开发人员&#xff08;Dev&#xff09;”和“IT运维技术人员&#xff08;Ops&#xff09;”…

通过限制访问,实现纯私有Docker镜像

怎么会不过审呢?没有敏感信息呀。 For obvious reasons,Many Docker image repositories are inaccessible,The official warehouse has also been filtered by the firewall,So write about how to build a self use Docker image using CloudFlares Workers and Pages. …

WPF MVVM使用遇见问题

一、遇见问题 1.使用Dictionary绑定ListBox的ItsSource问题 过程&#xff1a; 需要再界面动态显示字典&#xff0c;在循环中添加两条数据时&#xff0c;绑定的字典断点查看有两条&#xff0c;界面上只显示一条&#xff0c;后面再其他数量的都动态不显示&#xff0c;鼠标滚动后…

BUUCTF [安洵杯 2019]easy_serialize_php

这道题题目说easy但是对我来说极其不友好&#xff01;看了很多wp讲的模棱两可&#xff0c;我尽量来说清楚点 代码解析&#xff1a; 这里$function $_GET[f]&#xff0c;是我们通过get方式传递的&#xff0c;因为注释提示有东西先传fphpinfo看看 找到了一个东西&#xff0c;很…

IPD推行成功的核心要素(十五)项目管理提升IPD相关项目交付效率和用户体验

研发项目往往包含很多复杂的流程和具体的细节。因此&#xff0c;一套完整且标准的研发项目管理制度和流程对项目的推进至关重要。研发项目管理是成功推动创新和技术发展的关键因素。然而在实际管理中&#xff0c;研发项目管理常常面临着需求不确定、技术风险、人员素质、成本和…

STM32-寄存器DMA配置指南

配置步骤 在STM32F0xx中文参考手册中的DMA部分在开头给出了配置步骤 每个通道都可以在外设寄存器固定地址和存储器地址之间执行 DMA 传输。DMA 传输的数据 量是可编程的&#xff0c;最大达到 65535。每次传输之后相应的计数寄存器都做一次递减操作&#xff0c;直到 计数为&am…

电脑屏幕录制软件,分享4款(2024最新)

在今天&#xff0c;我们的电脑屏幕成为了一个多彩多姿的窗口。通过它我们可以浏览网页、观看视频、处理文档、进行游戏……有时&#xff0c;我们想要记录下这些精彩瞬间&#xff0c;与朋友分享&#xff0c;或者作为教程留存&#xff0c;这时&#xff0c;电脑屏幕录制就显得尤为…