文章目录
-
- 一、项目起航:项目初始化与配置
- [二、React 与 Hook 应用:实现项目列表](#二、React 与 Hook 应用:实现项目列表)
- [三、TS 应用:JS神助攻 - 强类型](#三、TS 应用:JS神助攻 - 强类型)
- 四、JWT、用户认证与异步请求
- [五、CSS 其实很简单 - 用 CSS-in-JS 添加样式](#五、CSS 其实很简单 - 用 CSS-in-JS 添加样式)
- [六、用户体验优化 - 加载中和错误状态处理](#六、用户体验优化 - 加载中和错误状态处理)
- [七、Hook,路由,与 URL 状态管理](#七、Hook,路由,与 URL 状态管理)
相对原教程,我在学习开始时(2023.03)采用的是当前最新版本:
项 | 版本 |
---|---|
react & react-dom | ^18.2.0 |
react-router & react-router-dom | ^6.11.2 |
antd | ^4.24.8 |
@commitlint/cli & @commitlint/config-conventional | ^17.4.4 |
eslint-config-prettier | ^8.6.0 |
husky | ^8.0.3 |
lint-staged | ^13.1.2 |
prettier | 2.8.4 |
json-server | 0.17.2 |
craco-less | ^2.0.0 |
@craco/craco | ^7.1.0 |
qs | ^6.11.0 |
dayjs | ^1.11.7 |
react-helmet | ^6.1.0 |
@types/react-helmet | ^6.1.6 |
react-query | ^6.1.0 |
@welldone-software/why-did-you-render | ^7.0.1 |
@emotion/react & @emotion/styled | ^11.10.6 |
具体配置、操作和内容会有差异,"坑"也会有所不同。。。
一、项目起航:项目初始化与配置
二、React 与 Hook 应用:实现项目列表
三、TS 应用:JS神助攻 - 强类型
四、JWT、用户认证与异步请求
五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
六、用户体验优化 - 加载中和错误状态处理
七、Hook,路由,与 URL 状态管理
1+2.
3~6
7.完成URL状态管理与JS中的 iterator讲解
searchParams
拿到了, 接下来用暴露出来的 setSearchParams
来替换 ProjectList
里的 setParam
修改 src\screens\ProjectList\index.tsx
:
js
...
export const ProjectList = () => {
const [param, setParam] = useUrlQueryParam(["name", "personId"]);
...
};
...
但是这样使用 setParam
时若是传入一个 { name1: 'Jack' }
的参数,没有任何报错拦截,这样肯定是不行的,所以需要在 setParam
即 setSearchParams
中使用对 key
的判断
修改 src\utils\url.ts
:
js
import { useMemo } from "react";
import { URLSearchParamsInit, useSearchParams } from "react-router-dom";
import { cleanObject } from "utils";
...
export const useUrlQueryParam = <K extends string>(keys: K[]) => {
const [searchParams, setSearchParams] = useSearchParams();
return [
useMemo(
() => keys.reduce((prev, key) => {
// searchParams.get 可能会返回 null,需要预设值来兼容
return { ...prev, [key]: searchParams.get(key) || "" };
// 初始值会对类型造成影响,需要手动指定
}, {} as { [key in K]: string }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[searchParams]
),
(params: Partial<{ [key in K]: unknown }>) => {
const o = cleanObject({ ...Object.fromEntries(searchParams), ...params }) as URLSearchParamsInit
return setSearchParams(o)
},
] as const;
};
- 遇到类似下面这样的类型不匹配问题,可以直接使用
as
来强制指定为提示的类型
类型"{ [x: string]: unknown; }"的参数不能赋给类型"URLSearchParamsInit | ((prev: URLSearchParams) => URLSearchParamsInit) | undefined"的参数。
通过 Object.fromEntries
引出 Iterator
的概念:
Symbol.iterator
为每一个对象定义了默认的迭代器。该迭代器可以被 for...of
循环使用。
在浏览器的 console
中做个小实验:
- 定义一个数组并使用
for..of
遍历
js
let arr = [1, 2, 3]
for(v of arr) { console.log(v) }
// 1
// 2
// 3
- 通过
Symbol.iterator
属性查看 此数组的 遍历器
js
arr[Symbol.iterator]
// ƒ values() { [native code] }
- 将其执行结果拿出来
js
let i = a[Symbol.iterator]()
i
// Array Iterator {}
// [[Prototype]]: Array Iterator
// next: ƒ next()
// Symbol(Symbol.toStringTag): "Array Iterator"
// [[Prototype]]: Object
- 可以看到它有个
next()
方法,执行一下
js
i.next()
// {value: 1, done: false}
i.next()
// {value: 2, done: false}
i.next()
// {value: 3, done: false}
i.next()
// {value: undefined, done: true}
- 接下来实现一下自定义遍历器
js
const obj = {
data: ["hello", "world"],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++] + "!",
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (let o of obj) {
console.log(o);
}
回归项目代码,searchParams
是 URLSearchParams
类型,通过以下代码可以看出使用 Object.fromEntries
可以将其(entries
)转为 object
js
new URLSearchParams({name: 'Jack'})[Symbol.iterator]
// ƒ entries() { [native code] }
代码逻辑明白了,接下来看下页面效果:
- http://localhost:3000/projects?name=骑手&personId=18 直接访问,参数在页面中保持
- 在页面中修改参数,URL 中同时更改,但有个小问题,下拉选择负责人时,页面中展示的是
personId
,接下来解决一下
在 src\screens\ProjectList\index.tsx
中打印 param
在 src\screens\ProjectList\components\SearchPanel.tsx
中打印 users
运行代码可以发现,param
中 id
是 string
但 users
中是 number
,没有很好兼容,暂时在src\screens\ProjectList\components\SearchPanel.tsx
中将 id
强制转换为 string
(String(user.id)
):
js
...
export const SearchPanel = ({ users, param, setParam }: SearchPanelProps) => {
return (
<Form css={{ marginBottom: "2rem", ">*": "" }} layout="inline">
...
<Form.Item>
<Select {...}>
<Select.Option value="">负责人</Select.Option>
{users.map((user) => (
<Select.Option key={user.id} value={String(user.id)}>...</Select.Option>
))}
</Select>
</Form.Item>
</Form>
);
};
查看页面效果,功能正常啦!
部分引用笔记还在草稿阶段,敬请期待。。。