Article Directory
-
- 1. Project launch: project initialization and configuration
- 2. React and Hook application: implement the project list
- 3. TS Application: JS God Assist - Strong Type
- 4. JWT, user authentication and asynchronous request
- 5. CSS is actually very simple - add styles with CSS-in-JS
- 6. User experience optimization - loading and error state handling
- 7. Hook, routing, and URL state management
Source of learning content: React + React Hook + TS Best Practice - MOOC
Compared with the original tutorial, I used the latest version at the beginning of my study (2023.03):
item | Version |
---|---|
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 |
The specific configuration, operation and content will be different, and the "pit" will also be different. . .
1. Project launch: project initialization and configuration
2. React and Hook application: implement the project list
3. TS Application: JS God Assist - Strong Type
4. JWT, user authentication and asynchronous request
5. CSS is actually very simple - add styles with CSS-in-JS
6. User experience optimization - loading and error state handling
7. Hook, routing, and URL state management
1+2.
3~6
7. Complete the explanation of URL state management and iterator in JS
searchParams
Got it, and then use the exposed one setSearchParams
to replace ProjectList
the one insetParam
Modify src\screens\ProjectList\index.tsx
:
...
export const ProjectList = () => {
const [param, setParam] = useUrlQueryParam(["name", "personId"]);
...
};
...
However, setParam
if you pass in a { name1: 'Jack' }
parameter when using in this way, there is no error interception, which will definitely not work, so you need setParam
to setSearchParams
use key
the judgment of in the immediate
Modify src\utils\url.ts
:
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;
};
- If you encounter a type mismatch problem like the following, you can directly use
as
to force the type specified as the prompt
类型“{ [x: string]: unknown; }”的参数不能赋给类型“URLSearchParamsInit | ((prev: URLSearchParams) => URLSearchParamsInit) | undefined”的参数。
Object.fromEntries
The concept introduced by Iterator
:
Symbol.iterator
A default iterator is defined for each object. This iterator can be for...of
recycled.
console
Do a little experiment in your browser :
- Define an array and
for..of
iterate over it using
let arr = [1, 2, 3]
for(v of arr) {
console.log(v) }
// 1
// 2
// 3
Symbol.iterator
A iterator to view this array via the property
arr[Symbol.iterator]
// ƒ values() { [native code] }
- Take out its execution result
let i = a[Symbol.iterator]()
i
// Array Iterator {}
// [[Prototype]]: Array Iterator
// next: ƒ next()
// Symbol(Symbol.toStringTag): "Array Iterator"
// [[Prototype]]: Object
- You can see that it has a
next()
method, execute it
i.next()
// {value: 1, done: false}
i.next()
// {value: 2, done: false}
i.next()
// {value: 3, done: false}
i.next()
// {value: undefined, done: true}
- Next, implement a custom traverser
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);
}
Online address: https://codesandbox.io/s/upbeat-wood-bum3j
The return item code searchParams
is URLSearchParams
of the type, and it can be seen from the following code that Object.fromEntries
it can be entries
converted ( ) intoobject
new URLSearchParams({
name: 'Jack'})[Symbol.iterator]
// ƒ entries() { [native code] }
The code logic is clear, let's look at the page effect:
- http://localhost:3000/projects?name=rider&personId=18 direct access, parameters are kept on the page
- Modify the parameters on the page, and change the URL at the same time, but there is a small problem. When the person in charge is selected from the drop-down list, the page is displayed. Let’s solve it
personId
next.
src\screens\ProjectList\index.tsx
print param in
src\screens\ProjectList\components\SearchPanel.tsx
print users in
Running the code, we can find that param
in id
is string
but users
in is number
, it is not very compatible, temporarily src\screens\ProjectList\components\SearchPanel.tsx
convert id
to string
( String(user.id)
):
...
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>
);
};
Check the page effect, the function is normal!
Some reference notes are still in draft stage, so stay tuned. . .