这周感觉过去的很快,周一到周五,咻的一下就没了。明天就是国庆节了,祝大家节日快乐✿✿ヽ(°▽°)ノ✿。(因为疫情,想去的地方又不能去了,属实有点难受。)
封装 atnd 的 select 组件
这个是之前做的一个需求,需要一个有表头和表尾的下拉框,类似于这样
当时我的想法是利用 Select 的 dropdownRender 属性,添加一个 table,简简单单就解决了,可是我花了几个小时实现了之后,才发现table 想要单独选中某一行的值,必须是 radio 或者 checkbox,效果出来就有点难受,大家可以自行看着 Table 脑补一下,今天抽了点时间去实现了之前没有做出来的东西。感兴趣的掘友可以自己粘贴代码运行一下。
先说说怎么使用吧,fieldProps 这个对象我们可以传递一些自己想要的属性,比如 placeholder、style、onChange等,options是数据源,labels是表头,你可以自己定义,也可以不传,组件内部会根据你传入的options获取表头需要的内容,onChange可以让我们获取到选择的值。
再来说说组件,主要分为两部分,一部分是表头部分,也就是代码的这一块;另一部分是下拉框的内容区域。
表头这里会获取输入的 lables,如果 lables 不存在的话,就去查询数据中的key,通过 reduce 高阶函数进行 整合和去重。dropdownRender 中的 menu 是 表的内容区域,取决于我们设置的 Option 组件的内容。
CustomSelect.tsx
import * as React from "react";
import { Select } from "antd";
const { Option } = Select;
interface IOption {
key?: string | number;
disabled?: boolean;
title?: string;
}
interface ILabels {
value: string;
style?: Record<string, any>;
}
interface IProps {
fieldProps?: Record<string, any>;
}
const CustomSelect = (props: IProps) => {
const { options, labels, ...restProps } = props?.fieldProps || {};
const [_key, ...allLabels] = options?.reduce(
(pre: string[], cur: Record<string, any>) => {
return new Set([...pre, ...Object.keys(cur)]);
},
[]
);
const headers = (labels || allLabels)?.map((item: string) => {
return {
value: item,
};
});
const width = restProps.style.width || 320;
const labelLength = headers.length;
const dropdownRender = (menu: React.ReactNode) => (
<>
<div
style={{
display: "flex",
flexDirection: "row",
width: width - 20,
margin: "0 10px 0",
}}
>
{headers?.map((label: ILabels, index: number) => {
const style: Record<string, any> =
index !== labelLength - 1
? {
flex: 1,
textAlign: "center",
borderRight: "1px solid #bfbfbf",
backgroundColor: "#fafafa",
}
: {
flex: 1,
textAlign: "center",
backgroundColor: "#fafafa",
};
return (
<div key={label.value} style={label.style || style}>
{label.value}
</div>
);
})}
</div>
{menu}
</>
);
return (
<>
<Select {...restProps} dropdownRender={dropdownRender}>
{options?.map((item: IOption) => {
const { key, title, disabled, ...rest } = item || {};
const keys: string[] = Object.keys(rest) || [];
return (
<Option
key={key || JSON.stringify(item)}
value={JSON.stringify(rest)}
title={title || JSON.stringify(rest)}
disabled={disabled || false}
>
<div
style={{
display: "flex",
flexDirection: "row",
}}
>
{keys.map((key: string) => {
return (
<div
key={key}
style={{
flex: 1,
textAlign: "center",
width: Math.floor((width - 20) / labelLength),
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{item[key]}
</div>
);
})}
</div>
</Option>
);
})}
</Select>
</>
);
};
export default CustomSelect;
App.tsx
import "./App.css";
import { Space, Button } from "antd";
import { Routes, Route } from "react-router-dom";
import Home from "./views/Home";
import About from "./views/About";
import Team from "./views/Team";
import CustomSelect from "./components/CustomSelect";
import React from "react";
interface valueType {
key: string | number;
name: string;
// age: number;
sex: string;
teacher: string;
}
interface IOption extends valueType {
disabled?: boolean;
title?: string;
}
const App = () => {
const options: IOption[] = [
{ key: 1, name: "jack", sex: "males", teacher: "benbnegou" },
{ key: 2, name: "licu", sex: "males", teacher: "benbnegou" },
{
key: 3,
name: "tom",
sex: "malesssssdvsebetnnem",
teacher: "benbengou",
},
];
const fieldProps = {
style: { width: 320 },
placeholder: "please select one person",
options,
onChange: (e: string) => {
console.log(e);
},
};
return (
<>
<Space>
<CustomSelect fieldProps={fieldProps} />
</Space>
</>
);
};
export default App;
Typescript
有关typescript,这周和之前做项目的时候,学到了一些typescript的 utility types,但是之前写博客没有想起来写,就在这期总结一下吧。
Record
Record 是TypeScript的一种工具类,表示严格的键值对。 更具体地说, Record<K,V> 表示对象只接受类型 K ,并且对应于这些键的值应该是类型 V 。 Record<K,V> 的键将产生 K 作为类型,而 Record<K,V> [K] 等价于 V。举个例子
我们在编写typescript代码的过程中,可能会遇到类似的情况,需要声明一个对象,然后这个对象的参数暂时是不知道的,原来我就是第一种写法,可是它飘红确实好难受,后来就改成了 any,可是再想想 我要写 typescript 而不是 anyscript。最后这种写法,真的不错!
如果我需要一个对象,有 name、age和sex 三个属性,属性的值必须是数字或者字符串,那么就可以这么写:
type key = 'name' | 'age' | 'sex'
const c: Record<key, string | number> = {
name: 'tom',
age: 20,
sex:'male',
}
console.log(c)
//{ "name": "tom", "age": 20, "sex": "male" }
也可以通过 keyof 来获取现有类型的所有属性,比如:
interface Staff {
name:string,
salary:string,
}
type StaffJson = Record<keyof Staff, string>
const product: StaffJson = {
name: 'John',
salary:'3000'
}
typeof、keyof 和 valueof(自定义)
在 TypeScript 中,typeof 操作符用来获取一个变量或对象的类型;keyof 操作符可以用于获取某种类型的所有键,其返回类型是联合类型
type ValueOf<T> = T[keyof T];
interface IStudent {
name: string
age: number
friends: string[]
teacherInfo: {
name: string
}
}
type StatusVal = ValueOf<IStudent>
type person = Record<keyof IStudent, StatusVal>
const p: person = {
name: 'tom',
age: 12,
friends: ['kang'],
teacherInfo: {
name:'zhudi'
}
}
type personCopy = typeof p
其中,typeof 返回对象 p 的 类型,keyof 返回 接口 IStudent 的 键的联合类型,
valueof 返回值的 联合类型
omit、pick、Partial、Required
假如我们现在有一个student的接口,
interface IStudent {
name: string
age: number
friends: string[]
teacherInfo: {
name: string
}
}
Omit去除类型中某些项
现在需要定义新的数据类型,去除 student 中的 老师信息
type person = Omit<IStudent, 'teacherInfo'>
function test(value: person): void {
console.log(value)
}
const p1: person = {
name: 'tom',
age: 12,
friends: ['michael']
}
test(p1)
源码
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Omit 构造一个除类型K外具有T性质的类型 参数为<type,string>;第一个为继承的type类型,第二个为想要的key的字符串,多个字符串用 | 分开
Pick选取类型中指定类型
现在需要定义新的数据类型,只要 student 中的 姓名和年龄
type person = Pick<IStudent, 'name' | 'age'>
function test(value: person): void {
console.log(value)
}
const p1: person = {
name: 'tom',
age: 12,
}
test(p1)
源码
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Partial将类型中所有选项变为可选,即加上?
现在需要定义新的数据类型,将 student 中的 老师信息 变成 可选
type person = Partial<IStudent>
function test(value: person): void {
console.log(value)
}
const p1: person = {
name: 'tom',
age: 12,
}
test(p1)
源码
/**
* Make all properties in T optional
*/
type Partial<T> = {
[P in keyof T]?: T[P];
};
Required将类型中所有选项变为必选,去除所有?
type person = Partial<IStudent>
function test(value: person): void {
console.log(value)
}
const p1: person = {
name: 'tom',
age: 12,
}
type person2 = Required<person>
test(p1)
源码
/**
* Make all properties in T required
*/
type Required<T> = {
[P in keyof T]-?: T[P];
};
React hooks
这些 hook 都是从公司学习来的,后续也会陆续更新。
useBoolean
该 hook 主要用来管理 Boolean 状态。体验demo
import * as React from "react";
type ReturnValue = [boolean, () => void, () => void, () => void];
const useBoolean = (defaultValue = false): ReturnValue => {
const [flag, setFlag] = React.useState<boolean>(defaultValue);
const setTrue = React.useCallback(() => {
setFlag(true);
}, [setFlag]);
const setFalse = React.useCallback(() => {
setFlag(false);
}, [setFlag]);
const toggle = React.useCallback(() => {
setFlag((bool) => !bool);
}, [setFlag]);
return [flag, setTrue, setFalse, toggle];
};
export default useBoolean;
useQuery
该 hook 主要用来获取 url 携带的参数。
import { useLocation } from "react-router-dom";
const useQuery = () => {
const { search } = useLocation();
const params = new URLSearchParams(search);
const query: Record<string, any> = {};
params?.forEach((param, key) => {
if (typeof query[key] === "undefined") {
query[key] = param;
} else {
const oldValues =
typeof query[key] === "string" ? [query[key]] : query[key];
query[key] = [...oldValues, param];
}
});
return [query];
};
export default useQuery;
参考资料
- www.typescriptlang.org/docs/handbo…
- TypeScript: Documentation - Keyof Type Operator (typescriptlang.org)
- TypeScript 常用语法 (Omit、Pick、Partial、Required)
- Hook API 索引 – React (docschina.org)
如有疑问或者错误,欢迎留言。