基于 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.mzph.cn/news/21019.shtml

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

相关文章

【Leetcode】73.矩阵置零

一、题目 1、题目描述 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例1: 输入:matrix = [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]示例2: 输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1…

设计模式九:组合模式(Composite Pattern)

组合模式是一种结构型设计模式&#xff0c;它允许我们将对象组合成树形结构来表示“整体-部分”层次关系。组合模式使得用户可以以相同的方式处理单个对象和组合对象。 在组合模式中&#xff0c;有两种主要类型的对象&#xff1a;叶子对象和组合对象。叶子对象代表树结构中的最…

vscode 插件::EIDE

最新最全 VSCODE 插件推荐&#xff08;2023版&#xff09;_vscode_白墨石-华为云开发者联盟 (csdn.net) 超好用的开发工具-VScode插件EIDE_vscode eide_桃成蹊2.0的博客-CSDN博客 Setup | Embedded IDE For VSCode (em-ide.com)

2023年华数杯数学建模C题思路代码分析 - 母亲身心健康对婴儿成长的影响

# 1 赛题 C 题 母亲身心健康对婴儿成长的影响 母亲是婴儿生命中最重要的人之一&#xff0c;她不仅为婴儿提供营养物质和身体保护&#xff0c; 还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况&#xff0c;如抑郁、焦虑、 压力等&#xff0c;可能会对婴儿的认知、情…

12、Citrix Xendesktop将 MCS 创建的计算机现有发布

目录 一、前言 二、XenServer解决方案 三、Vmware解决方案 一、前言 近期接到很多Case,原先MCS计算机目录损坏,里面的机器损坏,需要重新发布使用。 二、XenServer解决方案 Citrix Hypervisor (XenServer)

深入了解PostgreSQL:高级查询和性能优化技巧

在当今数据驱动的世界中&#xff0c;数据库的性能和查询优化变得尤为重要。 POSTGRESQL作为一种开源的关系型数据库管理系统&#xff0c;在处理大规模数据和复杂查询时表现出色。 但随着数据量和查询复杂性的增加&#xff0c;性能问题可能会显现出来。 本文将深入探讨POSTGR…

华为OD机试真题 Java 实现【寻找最大价值的矿堆】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、Java算法源码五、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》。 刷的越多…

2023年华数杯初步思路!

后续会不断更新&#xff0c;目前仅供参考&#xff0c;因为可能会不太完善。 有需要的可以私聊我&#xff0c;可能不会及时回&#xff0c;请耐心等待。 A题思路 A题的难度确实是比较大的&#xff0c;这是一道复杂的问题&#xff0c;涉及到物理学、材料科学和数学建模。首先&am…

面试题:请说下什么是重绘和重排(回流)?他们的区别是什么?

答&#xff1a; ● 第一次渲染 ○ html结构 解析为 dom树 ○ css样式 解析为 样式规则 ○ dom树 和 样式规则 匹配下&#xff0c;生成渲染树&#xff01; ○ 接下来就是重排&#xff1a;根据渲染树&#xff0c;得到每个盒子的几何信息&#xff08;大小位置&#xff09; ○ 最后…

Java面向对象之面向对象三大特性

文章目录 面向对象三大特性一、封装性二、继承性三、多态性 面向对象三大特性 一、封装性 为什么要有封装&#xff1f; 我们程序设计追求“高内聚&#xff0c;低耦合”。 高内聚 &#xff1a;类的内部数据操作细节自己完成&#xff0c;不允许外部干涉&#xff1b; 低耦合 &am…

Golang之路---03 面向对象——反射

反射 反射的存在意义 在开发中&#xff0c;你或许会碰到在有些情况下&#xff0c;你需要获取一个对象的类型&#xff0c;属性及方法&#xff0c;而这个过程其实就是反射。 golang中变量包括&#xff08;type, value&#xff09;两部分 静态类型 所谓的静态类型&#xff08;…

机器学习笔记 - 了解 GitHub Copilot 如何通过提供自动完成式建议来帮助您编码

一、GitHub Copilot介绍 GitHub Copilot 是世界上第一个大规模 AI 开发人员工具,可以帮助您以更少的工作更快地编写代码。GitHub Copilot 从注释和代码中提取上下文,以立即建议单独的行和整个函数。 研究发现 GitHub Copilot 可以帮助开发人员更快地编码、专注于解决更大的问…

第四次作业 运维高级 安装tomcat8和部署jpress应用

1. 简述静态网页和动态网页的区别。 静态网页 静态网页是指存放在服务器文件系统中实实在在的HTML文件。当用户在浏览器中输入页面的URL&#xff0c;然后回车&#xff0c;浏览器就会将对应的html文件下载、渲染并呈现在窗口中。早期的网站通常都是由静态页面制作的。 静态网页…

ip_vs 原理解析 (三)调度器

文章目录 调度器注册调度器绑定 svc ip_vs_schedule 结构体PE 调度器 ipvs 的 调度器&#xff08;scheduler&#xff09; 有很多种&#xff0c;这里不详细介绍各个调度器的算法&#xff0c;主要关注于 ipvs 流程中的调度器注册和使用。 ipvs 的调度器有 rr(轮询)&#xff0c;w…

Linux C 获取主机网卡名及 IP 的几种方法

在进行 Linux 网络编程时&#xff0c;经常会需要获取本机 IP 地址&#xff0c;除了常规的读取配置文件外&#xff0c;本文罗列几种个人所知的编程常用方法&#xff0c;仅供参考&#xff0c;如有错误请指出。 方法一&#xff1a;使用 ioctl() 获取本地 IP 地址 Linux 下可以使用…

【Spring框架】SpringBoot统一功能处理

目录 用户登录权限校验用户登录拦截器排除所有静态资源练习&#xff1a;登录拦截器拦截器实现原理 统一异常处理统一数据返回格式为什么需要统⼀数据返回格式&#xff1f;统⼀数据返回格式的实现 用户登录权限校验 用户登录拦截器 1.自定义拦截器 package com.example.demo.…

shell centos 7 一键部署 KVM软件脚本

这个脚本有限地方还需要完善下 设计思路&#xff1a; 1、创建检查内核函数 check_kernel() 2、创建升级内核函数 update_kernel() 3、创建检查是否支持虚拟化函数 check_virtual() 4、创建检查操作系统函数 check_system() 5、创建检查网络函数 check_network() 6…

MicroPython ESP32网页实时更新DHT11数据显示

MicroPython ESP32网页实时更新DHT11数据显示 &#x1f4cc;相关篇《MicroPython ESP32 读取DHT11温湿度传感器数据》&#x1f4cd;《【Micropython esp32/8266】网页点灯控制示例》 ✨本例综合以上两篇文章内容实现&#xff1a;在本地网页中显示DHT11温度传感器数据。可以做到…

【Clion 2】使用技巧

一、TODO: 说明&#xff1a; 有时需要标记部分代码以供将来参考&#xff1a; 优化和改进的领域、可能的更改、要讨论的问题等等。 支持&#xff1a; TODO和FIXME小写和大写。这些模式可以在任何受支持的文件类型的行注释和块注释内使用。 创建TODO项 在要添加注释的代码行中…

大数据课程F3——HIve的基本操作

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 掌握HIve的基本SQL语句和注意问题&#xff1b; ⚪ 掌握HIve的表结构&#xff1b; ⚪ 掌握HIve的数据类型&#xff1b; ⚪ 掌握HIve的基础函数和窗口函数&#xff1b; …