基于 Redux + TypeScript 实现强类型检查和对 Json 的数据清理

基于 Redux + TypeScript 实现强类型检查和对 Json 的数据清理

突然像是打通了任督二脉一样就用了 generics 搞定了之前一直用 any 实现的类型……

关于 Redux 的部分,这里不多赘述,基本的实现都在这里:Redux Toolkit 调用 API 的四种方式 和 async thunk 解决 API 调用的依赖问题。

之前的实现方法是这个:TS 使用自动提示生成对象中的键,不过实现了一下还是稍微麻烦了一些,而且 CRUD 中的 Create 操作比较难(因为缺乏默认值),之后还是换了一种写法。

虽然这里是用的是 react redux,不过因为不涉及到渲染部分,以及 redux 也可以在非 react 项目中使用,所以还是放到 TS 分类中了(x

下面就根据自己的经验和目前写的项目讲一下我自己琢磨出来的实现。

对象的类型定义

这种主要是通过 type or interface 去实现,具体没什么差别啦,不过对于我们来说,数据类型是固定的,没必要修改对应的数据类型,使用 type 就好了。具体用 type 还是 interface,还是具体需求具体分析。

另外 type 的优势就是少打一些字……?

具体实现如下:

export type IPost = {body: string;// 因为网上数据和项目数据格式不一致,所以这里暂时注释掉//   id: number;title: string;userId: number;
};// 这个跟 redux 存储状态相关
// export interface IPostModel extends ISimpleDataFormat<IPost> {}
export type IRdmMarketModel = ISimpleDataFormat<IRdmMarket>;

随后定义一个默认值:

export const defaultPost: IPost = {body: '',id: 0,title: '',userId: 0,
};

这样就完成了最初的设定。

Redux 的类型配置

Redux 的配置就比较复杂了,这一个也是比较项目相关的部分,下面也会一步步地进行拆解进行实现。

API 传来的数据类型

initialState 还是挺重要的,因为我们的 API 有十几二十多个,每一个的存储类型都是一致的,因此就不可能说将同样的东西 cv 十几二十遍。最终我设计的 Redux 存储格式为:

我们的业务是,后端绝对会返回一个 data 的数组,其中的数据类型大致为:

{"data": [{"type": "","id": 0,"attributes": {},// 并不一定存在"relationships": {}}],// include 也是外链关系,为relationship所包含的entity,数据类型与data一致"includes": {}
}

其中 type 为当前 entity 的名称,attributes 为当前 entity 所包含的属性,relationship 为可能存在的外链,includes 通过 query parameters 调用,为 relationship 中,外链对象的具体数据。

在具体存储的时候,我想把 id 放到 attributes 中,这样转化为数组给 UI 组件时会方便一些。于是,我定义了 ISimpleDataFormat 的类型:

export type IRelationship = {data: {type: string;id: number;};
};// 其实这个用 { [key: string]: IRelationship } 的效果应该也是一样的……?
export type IRelationships = Record<string, IRelationship>;export type ISimpleDataFormat<T> = {attributes: T & { id: string };relationships?: IRelationships;
};

relationship 我保存了后端传来的数据格式,一来没有特别的复杂,二来对于 create/update 操作,有的时候需要添加 relationship 进行双重验证和建立外链。

Redux 状态的设定

同样,我也对 Redux 存储的状态进行了标准化。首先它需要有一个 loading 状态,这样可以方便 UI 进行状态的更改,同时提醒用户正在拉去数据。其次需要错误的状态,同样也是为了显示在 UI 上,最后需要分别存储 data 和 included。经过讨论,最终决定以 K-V 对的方法存储数据。

最后的状态定义如下:

// 也可以使用 Record,我之后应该会以 Record 为主,毕竟写起来方便些
export type ReduxDataType<T> = { [key: string]: T };export type ISliceStateType<T, U> = {isLoading: boolean;error: null | SerializedError;data: ReduxDataType<T>;included: ReduxDataType<U>;
};export const initialSliceState = {isLoading: false,error: null,data: {},included: {},
};

其中 included 可能会存在多个不同的外链,不过目前我们只用到了一个,所以用 <T, U>,如果之后 included 出现两个以上的外链,那么……再改叭……

slice 的实现

这里主要讲一个 fetch,其他的操作基本一致,没什么特别大的区别。API 方面的话,就用网上的 dummy API 了:

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { initialSliceState } from '../sliceType';
import { ISimpleDataFormat, ISliceStateType } from '../../../types';
import { pick } from 'lodash';export type IPost = {body: string;title: string;userId: number;
};export interface IPostModel extends ISimpleDataFormat<IPost> {}export const defaultPost: IPost = {body: '',title: '',userId: 0,
};export const fetchPost = createAsyncThunk('posts/fetch', async () => {return fetch('https://jsonplaceholder.typicode.com/posts').then((response) => response.json()).then((json) => json);
});const postSlice = createSlice({name: 'rdmMarket',initialState: initialSliceState as ISliceStateType<IPostModel, unknown>,reducers: {},extraReducers(builder) {builder.addCase(fetchPost.fulfilled, (state, action) => {state.isLoading = false;action.payload.forEach((data: IPost & { id: string }) => {const id = String(data.id);const pickedAttributes = pick(data, Object.keys(defaultPost)) as IPost;const model: IPostModel = {attributes: {id,...pickedAttributes,},};state.data[id] = model;});});},
});export const postReducer = postSlice.reducer;

效果:

在这里插入图片描述

在这里插入图片描述

这里的数据相对而言比较简单,因此说使用 pick 对 model 的操作好像有些多余。不过对我们项目来说:

  1. 后端会自动生成一堆前端用不到的 key,比如说创建时间、更新时间、创建对象、版本等,这些东西前段用不到,存储在状态里就是占用无谓的空间。我们项目数据量还比较大,有的时候会有十几二十来万的数据(2b 项目,非 2c),所以能做一点优化就做一点优化。
  2. 多余的数据传到后端会被 rejected 掉,所以对数据的过滤是必须的

在这里插入图片描述

如果说一些属性是不需要的,直接在 interface 中删掉,TS 就会自动报错了。


类型检查主要还是防止 typo 以及写代码更方便一些,比如说:

在这里插入图片描述

在这里插入图片描述

我们项目的数据都是强定形的,所以对我们项目来说使用 TS 的优势绝对比使用 JS 多……尤其是有些 entity 会有一百多个 attributes,这时候如果没有一些智能提示或者是类型检查,报错都得掉一堆头发

是的,真的发生过,后来发现就因为是 typo 所以拿不到值……还有就是后端改了一些数据类型,然后前端没有在所有的组件中全部更新,导致有些页面出现问题有些事正常的。也就是那时候我们决定要落实 TS 的实现(虽然到最后只有我一个人在认真想怎么重构,猪队友都在用 any……痛苦……

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

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

相关文章

机器学习概述及其主要算法

目录 1、什么是机器学习 2、数据集 2.1、结构 3、算法分类 4、算法简介 4.1、K-近邻算法 4.2、贝叶斯分类 4.3、决策树和随机森林 4.4、逻辑回归 4.5、神经网络 4.6、线性回归 4.7、岭回归 4.8、K-means 5、机器学习开发流程 6、学习框架 1、什么是机器学习 机器…

Emacs之set-face-attribute与font-lock-add-keywords用法区别(一百二十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

在java中如何使用openOffice进行格式转换,word,excel,ppt,pdf互相转换

1.首先需要下载并安装openOffice,下载地址为&#xff1a; Apache OpenOffice download | SourceForge.net 2.安装后&#xff0c;可以测试下是否可用&#xff1b; 3.build.gradle中引入依赖&#xff1a; implementation group: com.artofsolving, name: jodconverter, version:…

笔记:Android 9系统启动流程

启动流程 1.按下电源&#xff0c;系统启动 当电源键按下时&#xff0c;引导芯片代码&#xff08;汇编指令&#xff09;会从预定的地方&#xff08;固化在ROM&#xff09;开始执行&#xff0c;将引导程序 BootLoader 加载到 RAM中&#xff0c;然后执行 2.引导程序 BootLoader …

【3维视觉】3D空间常用算法(点到直线距离、面法线、二面角)

3D空间点到直线的距离 3D空间点到直线的距离 3D空间的曲率 三维空间有三个基本元素&#xff0c;点&#xff0c;线&#xff0c;面。那么曲率是如何定义的呢&#xff1f; 点的曲率&#xff1f; 线的曲率&#xff1f; 面的曲率&#xff1f; 法曲率 设曲面上的曲线在某一点处的切…

Unity 使用SharpZipLib解压时报错

报错信息&#xff1a; NotSupportedException: Encoding 936 data could not be found. Make sure you have correct international System.Text.Encoding.GetEncoding (System.Int32 codepage) ICSharpCode.SharpZipLib.Zip.ZipConstants.ConvertToString。 出现问题分析&…

【SpringBoot】日志是+基于lombok的日志输出

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 在我们日常的程序开发中&#xff0c;日志是程序的重要组成部分&#xff0c;想象⼀下&#xff0c;如果程序报错了&#xff0c;不让你打开控制台看⽇志&#xff0c;那么你能找到报错的原因吗…

leetcode37. 解数独(java)

解数独 解数独题目描述回溯算法代码演示 回溯算法 解数独 难度 困难 leetcode37. 解数独 题目描述 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 1.数字 1-9 在每一行只能出现一次。 2.数字 1-9 在每一列只能出现一次。 3.数字…

【C++】开源:ncurses终端TUI文本界面库

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍ncurses终端文本界面库。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下…

【2023年电赛国一必备】E题报告模板--可直接使用

创作不易&#xff0c;麻烦关注CSDN【技术交流、免费报告资料】 通过百度网盘分享的文件&#xff1a;https://pan.baidu.com/s/1aXzYwLMLx_b59abvplUiYw?pwddn71 提取码:dn71 复制这段内容打开「百度网盘APP 即可获取」 任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部…

解决word打字卡顿问题的方法

❤ 2023.8.5 ❤ 最近整理论文&#xff0c;本来我是wps死忠粉&#xff0c;奈何wps不支持latex公式。。。 无奈用起了word&#xff0c;但是谁想字数稍微多了一点&#xff0c;word就卡得欲仙欲死&#xff0c;打个字过去2s才显示出来&#xff0c;删除的时候都不知道自己删了几个字…

应用启动:OOM command not allowed when used memory > ‘maxmemory‘

问题描述 应用启动的时候出现报错&#xff1a;OOM command not allowed when used memory &#xff1e; ‘maxmemory‘ 根据报错信息&#xff0c;应该是redis内存不够导致的异常。 查看缓存淘汰策略及内存使用&#xff1a; 设置缓存淘汰策略 Redis缓存淘汰策略是指在Redi…

替换开源LDAP,西井科技用宁盾目录统一身份,为业务敏捷提供支撑

客户介绍 上海西井科技股份有限公司成立于2015年&#xff0c;是一家深耕于大物流领域的人工智能公司&#xff0c;旗下无人驾驶卡车品牌Q-Truck开创了全球全时无人驾驶新能源商用车的先河&#xff0c;迄今为止已为全球16个国家和地区&#xff0c;120余家客户打造智能化升级体验…

OpenUSD联盟:塑造元宇宙的3D未来

一、引言 近日&#xff0c;美国3D内容行业的五家主要公司苹果、英伟达、皮克斯、Adobe和Autodesk联合成立了OpenUSD联盟&#xff08;AOUSD&#xff09;。这一联盟的成立标志着元宇宙领域的一次重要合作&#xff0c;旨在制定元宇宙的3D图形标准。本文将深入探讨OpenUSD联盟的目…

解决运行flutter doctor --android-licenses时报错

问题描述&#xff1a; 配置flutter环境时&#xff0c;会使用flutter doctor命令来检查运行flutter的相关依赖是否配好。能看到还差 Android license status unknown.未解决。 C:\Users\ipkiss.wu>flutter doctor Flutter assets will be downloaded from https://storage.…

【Linux】五种IO模型

文章目录 1. IO基本概念2. 五种IO模型2.1 五个钓鱼的例子2.2 五种IO模型2.2.1 阻塞IO2.2.2 非阻塞IO2.2.3 信号驱动IO2.2.4 IO多路转接2.2.5 异步IO 1. IO基本概念 认识IO IO就是输入和输出&#xff0c;在冯诺依曼体系结构中&#xff0c;将数据从输入设备拷贝到内存就叫输入&am…

GO语言的垃圾回收机制

内存垃圾的产生 程序在内存上被分为堆区、栈区、全局数据区、代码段、数据区五个部分。对于C等早期编程语言栈上的内存回由编译器负责管理回收&#xff0c;而堆上的内存空间需要编程人员负责申请和释放。在Go中栈上内存仍由编译器负责管理回收&#xff0c;而堆上的内存由编译器…

[腾讯云Cloud Studio实战训练营]无门槛使用GPT+Cloud Studio辅助编程完成Excel自动工资结算

目录 前言一、Cloud Studio产品介绍1.1 注册Cloud Studio 二、项目实验2.1 选择合适的开发环境2.2 实验项目介绍2.3 实验步骤三、总结 前言 chatgpt简单介绍: ChatGPT是一种基于GPT的自然语言处理模型&#xff0c;专门用于生成对话式文本。它是OpenAI于2021年发布的&#xff0…

sql语句字符函数,数学函数

一、trim&#xff08;&#xff09;去掉前后单元格 SELECT LENGTH(TRIM( 张三 )) AS 姓名 trim&#xff08;aa from bb) 除掉bb中前后包含的aa&#xff0c;中间的保留 SELECT TRIM(班 FROM class) AS 姓名 FROM user_test 二、lpad&#xff08;&#xff09;用指定字符做左…

【雕爷学编程】MicroPython动手做(30)——物联网之Blynk 3

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…