[React 进阶系列] React Context 案例学习:使用 TS 及 HOC 封装 Context

[React 进阶系列] React Context 案例学习:使用 TS 及 HOC 封装 Context

具体 context 的实现在这里:[React 进阶系列] React Context 案例学习:子组件内更新父组件的状态。

根据项目经验是这样的,自从换了 TS 之后,就再也没有二次封装过了使用 TS 真的可以有效解决 typo 和 intellisense 的问题

这里依旧使用一个简单的 todo 案例去完成

使用 TypeScript

结构方面采用下面的结构:

❯ tree src
src
├── App.css
├── App.test.tsx
├── App.tsx
├── context
│   └── todoContext.tsx
├── hoc
├── index.css
├── index.tsx
├── logo.svg
├── models
│   └── todo.type.ts
├── react-app-env.d.ts
├── reportWebVitals.ts
└── setupTests.ts4 directories, 11 files

创建 type

这里的 type 指的是 Todo 的类型,以及 context 类型,这是一个简单案例,结构就不会特别的复杂:

  • todo.type.ts

    export type ITodo = {id: number;title: string;description: string;completed: boolean;
    };
    
  • todoContext.tsx

    import { ITodo } from '../models/todo.type';export type TodoContextType = {todos: ITodo[];addTodo: (todo: ITodo) => void;removeTodo: (id: number) => void;toggleTodo: (id: number) => void;
    };
    

    这种 type 的定义,我基本上说 component 在哪里就会定义在哪里,而不会单独创建一个文件在 model 下面去实现,当然,这种其实也挺看个人偏好的……

创建 context

这里主要就是提供一个 context,以供在其他地方调用 useContext 而使用:

export const TodoContext = createContext<TodoContextType | null>(null);

这里 <> 是接受 context 的类型,我个人偏向会使用一个具体的 type 以及 null。其原因是 JS/TS 默认没有初始化和没有实现的变量都是 undefined,也因此使用 undefined 的指向性不是很明确。

而使用 null 代表这个变量存在,因此更具有指向性

虽然在 JS 实现中一般我都偷懒没设置默认值……

没有报错真的会忘……超小声 bb

创建 Provider

Provider 的实现如下:

const TodoProvider: FC<{ children: ReactNode }> = ({ children }) => {const [todos, settodos] = useState<ITodo[]>([{id: 1,title: 'Todo 1',completed: false,description: 'Todo 1',},{id: 2,title: 'Todo 2',completed: false,description: 'Todo 1',},]);const addTodo = (todo: ITodo) => {const newTodo: ITodo = {id: todos.length + 1,title: todo.title,description: todo.description,completed: false,};settodos([...todos, newTodo]);};const removeTodo = (id: number) => {const newTodos = todos.filter((todo) => todo.id !== id);settodos(newTodos);};const toggleTodo = (id: number) => {const newTodos = todos.map((todo) => {if (todo.id === id) {return { ...todo, completed: !todo.completed };}return todo;});settodos(newTodos);};return (<TodoContext.Providervalue={{todos,addTodo,removeTodo,toggleTodo,}}>{children}</TodoContext.Provider>);
};export default TodoProvider;

其实也没什么复杂的,主要就是一个 FC<ChildPops> 这个用法,这代表当前的函数是一个 Functional Component,它只会接受一个参数,并且它的参数会是一个 ReactNode

添加 helper func

如果想要直接使用 const {} = useContext(TodoContest) 的话,TS 会报错——默认值是 null。所以这个时候可以创建一个 helper func,保证返回的 context 一定有值:

export const useTodoContext = () => {const context = useContext(TodoContext);if (!context) {throw new Error('useTodoContext must be used within a TodoProvider');}return context;
};

这样可以保证调用 useTodoContext 一定能够获取值。两个错误对比如下:

在这里插入图片描述

在这里插入图片描述

不过这个时候页面渲染还是有一点问题,因为没有提供对应的 provider:

在这里插入图片描述

使用 HOC

一般的解决方法有两种:

  1. 直接在 Main 上嵌套一个 Provider

    这个的问题就在于,Main 本身就需要调用 context 中的值,如果在这里嵌套的话就会导致 Main 组件中无法使用 context 中的值

  2. 在上层组件中添加对应的 provider

    这样得到 App 层去修改,可以,但是有的情况并不是一个非常的适用,尤其是多个 Provider 嵌套,而其中又有数据依赖的情况下,将 Provider 一层一层往上推意味着创建多个 component 去实现

使用 HOC 的方法是兼具 1 和 2 的解决方案,具体实现如下:

import { ComponentType } from 'react';
import TodoProvider, { TodoContextType } from '../context/todoContext';const withTodoContext = (WrappedComponent: ComponentType<any>) => () =>(<TodoProvider><WrappedComponent /></TodoProvider>);export default withTodoContext;

这样 Main 层面的调用如下:

import React from 'react';
import { Button } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import { useTodoContext } from '../context/todoContext';
import withTodoContext from '../hoc/withTodoContext';const Main = () => {const { todos } = useTodoContext();return (<div className="todo-main"><input type="text" /><Button className="add-btn"><AddIcon /></Button><br /><ul>{todos.map((todo) => (<li key={todo.id}>{todo.title}</li>))}</ul></div>);
};export default withTodoContext(Main);

补充一下,如果 HOC 需要接受参数的话,实现是这样的:

const withExampleContext =(WrappedComponent: ComponentType<any>) => (moreProps: ExampleProps) =>(<ExampleProvider><WrappedComponent {...moreProps} /></ExampleProvider>);export default withExampleContext;

这样导出的方式还是使用 withExampleContext(Component),不过上层可以用 <Component prop1={} prop2={} /> 的方式向 Component 中传值

调用

完整实现如下:

const Main = () => {const [newTodo, setNewTodo] = useState('');const { todos, addTodo, toggleTodo } = useTodoContext();const onChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {setNewTodo(e.target.value);};const onAddTodo = () => {addTodo({title: newTodo,description: '',completed: false,});setNewTodo('');};const onCompleteTodo = (id: number) => {toggleTodo(id);};return (<div className="todo-main"><input type="text" value={newTodo} onChange={onChangeInput} /><Button className="add-btn" onClick={onAddTodo}><AddIcon /></Button><br /><ul>{todos.map((todo) => (<likey={todo.id}style={{textDecoration: todo.completed ? 'line-through' : 'none',cursor: 'pointer',}}onClick={() => onCompleteTodo(todo.id)}>{todo.title}</li>))}</ul></div>);
};export default withTodoContext(Main);

效果如下:

在这里插入图片描述

TS 提供的自动提示如下:

在这里插入图片描述

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

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

相关文章

光线追踪12 - Defocus Blur(虚焦模糊)

现在我们的最后一个特性是虚化模糊。注意&#xff0c;摄影师通常称之为景深&#xff0c;所以请确保在光线追踪的朋友中只使用虚化模糊这个术语。 真实相机具有虚化模糊是因为它们需要一个大孔&#xff08;而不仅仅是针孔&#xff09;来收集光线。一个大孔会导致所有物体失去焦点…

社交媒体革新者:揭秘Facebook对在线互动的影响

1. Facebook的兴起与发展 Facebook由马克扎克伯格在哈佛大学宿舍创建&#xff0c;最初只是服务于哈佛大学学生的社交网络。然而&#xff0c;其后快速扩张到其他大学和全球&#xff0c;成为了全球最大的社交媒体平台之一。其发展历程不仅是数字时代的典范&#xff0c;也是创业成…

CrySiS勒索病毒最新变种来袭,加密后缀为kharma

CrySiS勒索病毒&#xff0c;又称Dharma&#xff0c;首次出现是在2016年&#xff0c;2017年5月此勒索病毒万能密钥被公布之后&#xff0c;之前的样本可以解密&#xff0c;导致此勒索病毒曾消失了一段时间&#xff0c;不过随后又马上出现了它的一款最新的变种样本&#xff0c;加密…

101 向一个不存在的路径发送请求, get 得到 404, post 得到 405

前言 这是 最近碰到的一个问题, 大概是在 2022.05.30 前端这边 发送了一个业务请求过来, 这个请求路径是服务端这边不存在的 但是 奇怪的一点就是, 如果是以 get 请求发送过来, 服务端响应的是正确的 404 "Not Found", 但是 如果是以 post 请求发送过来, 服务端这边…

Springboot整合rabbitmq(二)

spring boot&#xff0c;为了简化rabbitMQ的使用&#xff0c;它在这里面给咱们提供了一个模板对象。 这个模板对象是什么&#xff1f; 这个模板对象叫RabbitTemplate对象。说白了其实是用来简化rabbitMQ的操作 也就是说之后我们可以通过这个模板对象直接去向rabbitMQ中发送消息…

1分钟做的AI利润表分析报告,效果怎样?

各位数据的朋友&#xff0c;大家好&#xff0c;我是老周道数据&#xff0c;和你一起&#xff0c;用常人思维数据分析&#xff0c;通过数据讲故事&#xff01; 自从去年年初ChatGPT3.5推出以来&#xff0c;AI大模型的话题就一直非常热了。相比于AI聊天、生成图片&#xff0c;亦…

STL空间配置器

参考《STL源码剖析-侯捷》一书 (SGI版本STL) 前置 六大组件 空间配置器实现 SGI版本的空间配置器有两个&#xff0c;一个名为allocator&#xff0c;一个名为alloc。前者符合部分标准&#xff0c;但效率不好&#xff0c;只是对operator new和operator delete进行了封装&#…

工业互联网平台的专题报告

文 | BFT机器人 前言&#xff1a; 现在是工业时代&#xff0c;也是数字化时代。随着信息技术的快速发展&#xff0c;工业不得不依托数字化转型升级。当前数字化转型已经成为企业提升竞争力的关键手段。 工业互联网平台作为数字化转型的重要支撑&#xff0c;能够帮助企业实现生…

(day 2)JavaScript学习笔记(基础之变量、常量和注释)

概述 这是我的学习笔记&#xff0c;记录了JavaScript的学习过程&#xff0c;我是有一些Python基础的&#xff0c;因此在学习的过程中不自觉的把JavaScript的代码跟Python代码做对比&#xff0c;以便加深印象。我本人学习软件开发纯属个人兴趣&#xff0c;大学所学的专业也非软件…

Linux系统编程(六)高级IO

目录 1. 阻塞和非阻塞 IO 2. IO 多路转接&#xff08;select、poll、epoll&#xff09; 3. 存储映射 IO&#xff08;mmap&#xff09; 4. 文件锁&#xff08;fcntl、lockf、flock&#xff09; 5. 管道实例 - 池类算法 1. 阻塞和非阻塞 IO 阻塞 IO&#xff1a;会等待操作的…

猫咪挑食怎么办?预防猫咪挑食的生骨肉冻干分享

在现今社会&#xff0c;养猫的人越来越多&#xff0c;大家都把自家的小猫当作宝贝来宠爱。然而&#xff0c;这种宠爱有时也会导致猫咪养成挑食的不良习惯。那么&#xff0c;猫咪挑食怎么办呢&#xff1f; 今天&#xff0c;我要分享一个既能确保猫咪不受苦&#xff0c;又能有效…

嵌入式学习第二十六天!(网络传输:TCP编程)

TCP通信&#xff1a; 1. TCP发端&#xff1a; socket -> connect -> send -> recv -> close 2. TCP收端&#xff1a; socket -> bind -> listen -> accept -> recv -> send -> close 3. TCP需要用到的函数&#xff1a; 1. co…

MySQL--索引底层数据结构详解

索引是什么&#xff1f; 索引是帮助MySQL高效获取数据的排好序的数据结构&#xff0c;因此可知索引是数据结构。 概念很抽象&#xff0c;但是类比生活中的例子就很容易理解&#xff0c;比如一本厚厚的书&#xff0c;我们想取找某一小节&#xff0c;我们可以根据目录去快速找到…

Python实现快速排序算法

Python实现快速排序算法 下面是使用 Python 实现的快速排序算法的示例代码&#xff1a; def quick_sort(arr):if len(arr) < 1:return arrelse:pivot arr[0]less_than_pivot [x for x in arr[1:] if x < pivot]greater_than_pivot [x for x in arr[1:] if x > pi…

Spring Boot中Excel数据导入导出的高效实现

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

基于SpringBoot+MYSQL的大学生租房平台

目录 1、 前言介绍 2、主要技术 3、系统流程 3.1、操作流程 3.2、登录流程 3.3、删除信息流程 3.4、添加信息流程 4、功能需求 5、系统设计 5.1、功能结构设计 5.1、数据库概念设计 6、运行截图(部分) 6.1、管理员功能实现 6.1.1、房东管理 6.1.2、信息审批管理 …

Redis 配置文件详解

Units 单位 配置大小单位&#xff0c;开头定义了一些基本的度量单位&#xff0c;只支持bytes&#xff0c;不支持bit&#xff0c;大小写不敏感。 # Redis configuration file example. # # Note that in order to read the configuration file, Redis must be # started with …

聚观早报 | 腾讯QQ测试AI对话功能;哪吒L官宣4月交付

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 3月8日消息 腾讯QQ测试AI对话功能 哪吒L官宣4月交付 中国对瑞士等6国试行免签 Redmi K70至尊版细节曝光 Meta正…

IDEA自定义Maven仓库

Maven 是一款广泛应用于 Java 开发的工具&#xff0c;其作用类似于一个全自动的 JAR 包管理器&#xff0c;能够方便地导入开发所需的相关 JAR 包。在使用 Maven 进行 Java 程序开发时&#xff0c;开发者能够极大地提高开发效率。以下是关于如何安装 Maven 以及在 IDEA 中配置自…

基于LSTM实现春联上联对下联

按照阿光的项目做出了学习笔记&#xff0c;pytorch深度学习实战项目100例 基于LSTM实现春联上联对下联 基于LSTM&#xff08;长短期记忆网络&#xff09;实现春联上联对下联是一种有趣且具有挑战性的任务&#xff0c;它涉及到自然语言处理&#xff08;NLP&#xff09;中的序列…