杂项:
1、子组件children类型是 ReactNode
export const AppProviders = ({ children }: { children: ReactNode }) => {
return <AuthProvider>{children}</AuthProvider>;
};
2、axios 和 fetch 的表现不一样,axios可以直接在返回状态不为2xx的时候抛出异常
3、函数参数中使用了结构赋值,就不能用?:可选功能,解决方法是赋予一个默认值
{ data, token, headers, ...customConfig }: Config = {}
4、将emotion格式应用到行内样式
/** @jsx jsx */
import { jsx } from "@emotion/react";
<Form css={{ marginBottom: "2rem" }} layout={"inline"}>
5、svg以组件形式渲染
import { ReactComponent as SoftwareLogo } from "assets/software-logo.svg";
<SoftwareLogo width={"18rem"} color={"rgb(38, 132, 255)"} />
6、由于ts的特殊性,object类型的值可被赋予任何类型的对象
let a: object
a = {name: 'jack'}
a = () => {
}
a = new RegExp('')
如果指定想要键值对类型的对象,可以定义为:object: { [key: string]: unknown }
7、压制eslint报错
eslint-disable-next-line react-hooks/exhaustive-deps
8、type 'Project[] | null' is not assignable to type 'Project[] | undefined'
解决方法:赋值时加一个[],例如 'list || []'
9、type 获取函数传入的参数类型:
Parameters<typeof 函数名>
(...[endpoint, config]: Parameters<typeof http>) => { do something }
[key: string] 和 [key in string] 区别
- [key: string] 的类型可能会被意外改变,[key in string]则不会
type T1 = {[key: string]: null};
type T2 = {[key in string]: null};
const t1: T1 = {'foo': null, 10: null};
const t2: T2 = {'foo': null, 10: null};
type S1 = keyof T1; // string | number
type S2 = keyof T2; // string
const s1: S1 = 10;
const s2: S2 = 10; // error
- 前者支持可选,后者不支持
type T1 = {[key: string]: null};
type T2 = {[key in string]: null};
type T1opt = {[key: string]?: null}; // invalid syntax
type T2opt = {[key in string]?: null};
typescript extends 使用
参考:Typescript中的extends关键字-技术圈 (proginn.com)
泛型约束
对类型参数作一定的限制,比如希望传入的参数都有 name 属性的数组
function getCnames<T extends { name: string }>(entities: T[]):string[] {\
return entities.map(entity => entity.cname)\
}
条件类型与高阶类型
用来判断一个类型是不是可以分配给另一个类型
type Human = {\
name: string;\
}\
type Duck = {\
name: string;\
}\
type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'yes'
一、项目启动和配置
启动:
npx create-react-app jira --template typescript
npm start
配置:
- 设置绝对路径
在tsconfig.json里添加"baseUrl":"./src",意思是将'./src'设置成绝对路径:
- prettier 统一代码规范 Install · Prettier 中文网
npm install --save-dev --save-exact prettier
新建配置文件:echo {}> .prettierrc.json
新建.prettierignore文件,指定不需要格式化的文件。写入:
# Ignore artifacts:
build
coverage
通过npm prettier --write .即可实现手动格式化。
Pre-commit Hook 可以让代码在每次提交commit前自动进行一次格式化
- 将prettier配置在git hook里,让开发团队的人push时都格式化
需要用到工具npx mrm lint-staged (不行的用 npx mrm@2 lint-staged)
安装完后会多出.husky文件夹,git提交前别忘记add git add .husky
在package.json里添加ts和tsx类型文件的校验:
- eslint相关
prettier和eslint一起工作会发生冲突,所以需要额外工具
yarn add eslint-config-prettier -D
在package.json里添加配置,代表用prettier覆盖一部分原来的规则:
- 规范commitmessage
用到工具
commitlintconventional-changelog/commitlint: 📓 Lint commit messages (github.com):
yarn add @commitlint/config-conventional @commitlint/cli -D
生成配置文件:
echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js
npx husky add .husky/commit-msg "yarn commitlint --edit $1" ,或者手动添加配置文件:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
#--no-install 参数表示强制npx使用项目中node_modules目录中的commitlint包
npx --no-install commitlint --edit $1
1. feat: 添加代码和逻辑, 如 feat: add xxx field/method/class
2. fix: 修复bug,如 fix: #123, fix xxx error
3. docs: 文档更新,如 docs: change documents
4. style: 样式修改,如 style: add class or change style
5. refactor: 代码重构, 如refactor: rename, move, extract, inline等
6. perf: 代码性能优化,perf: improves performance
7. test: 代码单元测试,test: test menu component
8. build: 项目构建,build: build project
9. ci: 修改CI文件 ci: change gitlab-ci.yml
10. chore: 构建过程或辅助工具的变动 chore: change webpack
mock 数据,用到json-server:
yarn add json-server -D
添加 __json_server_mock__/db.json,写入
{
"users": []
}
在package.json中添加scripts:
"json-server": "json-server __json_server_mock__/db.json --watch",
npm run json-server即可启动服务
二、自定义hook
三、ts
yarn add @types/qs -D 安装ts版本的qs(用于将对象解析成uri的形式)
3.1 范型
不确定具体要使用什么类型的时候,就用范型,范型是使用的时候,才确定他的类型是什么
3.2 TS Utility Types
类型别名和interface
type favorite = string | number;
let myFavorite: favorite = 6;
interface 无法实现混合类型,也没法实现utilityType
utility type用法:用范型给它传入其他类型,然后utility type对其进行操作
type Person = {
name: string,
age: number
}
const xiaoMing: Partial<Person> = {}; // 可选
const shenMiRen: Omit<Person, 'name' | 'age'> = {} // 删除属性
用到pick exclude
四、用户认证与异步请求
4.1 模拟真实服务器
通过mockServiceWorker实现的 npx imooc-jira-tool
npx msw init public 去掉warning
更新:npm install jira-dev-tool@next,修改index.tsx和context/index.tsx
四、css in js
antd
安装后在src/index.tsx中导入import 'antd/dist/antd.less';,放在下面,覆盖 jira-dev-tool 已有样式
修改原配置,以主题色为例
安装yarn add @craco/craco,用于修改原有配置
将 package.json 中 react-scripts 修改为 craco
添加及修改package.json配置文件。在 create-react-app 中使用 - Ant Design
样式(使用emotion)
安装 yarn add @emotion/react @emotion/styled
何时使用flex 和 grid:
/**
* grid 和 flex 各自的应用场景
* 1. 要考虑,是一维布局 还是 二维布局
* 一般来说,一维布局用flex,二维布局用grid
* 2. 是从内容出发还是从布局出发?
* 从内容出发:你先有一组内容(数量一般不固定),然后希望他们均匀的分布在容器中,由内容自己的大小决定占据的空间
* 从布局出发:先规划网格(数量一般比较固定),然后再把元素往里填充
* 从内容出发,用flex
* 从布局出发,用grid
*
*/
yarn add jira-dev-tool@next
五、hooks
5.1 标签title
yarn add react-helmet
yarn add -D @types/react-helmet
5.2 hook 与 闭包
export const Test = () => {
const [num, setNum] = useState(0);
const add = () => setNum(num + 1);
useMount(() => {
setInterval(() => {
console.log('num in setInterval', num)
}, 1000)
})
useEffect(() => {
return () => {
console.log(num);
}
}, [])
} // 输出的都是1
页面被加载时候执行了一次,回调函数里引用的是页面加载时的num值
另一例 模拟 useEffect:
const test = () => {
let num = 0;
const effect = () => {
num += 1;
const message = `num value in message: ${num}`
return function unmount(){
console.log(message);
}
}
return effect;
}
const add = test(); // 这里返回的是effect函数
const unmount = effect(); // mssage1
add(); // message2
add(); // message3
add(); // message4
unmount(); // message1
路由
yarn add react-router@6 react-router-dom@6
yarn add history
工具,辅助查看重复渲染的原因:
yarn add --dev @welldone-software/why-did-you-render
无限循环及类似bug
8-1 8-2
8-6 钩子函数 react hooks 中 useEffect, useMemo 等依赖的坑:基本类型,可以放到依赖里;组件状态,可以放到依赖里;非组件状态的对象,绝不可以放到依赖里
9-4 useState 中存函数:
涉及到惰性初始state,useState中存的函数会在组件初次渲染时立即执行(setState也会立即执行),后续渲染就不会再执行。
如果要用useState存函数,需要用函数再包一层。通过useRef可以实现相同的效果(注:useRef存的变量相当于一个普通变量,通过.current修改不会引起页面重新渲染,并且读.current的操作只会在第一次渲染时发生)。
想要在setState后立刻调用,这样做:setState(value, () => {// do something})
组件卸载后异步请求没结束,试图修改state
10-1
利用钩子函数解决:
/**
* 返回组件的挂载状态,如果还没挂载或者已经卸载,返回false;反之,返回true
*/
export const useMountedRef = () => {
const mountedRef = useRef(false);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
});
return mountedRef;
};
useEffect 缺少依赖的提示
使用useCallback解决
新坑:useCallback里用到state,但重新设置了state,导致重复渲染
解决方案:在设置state时候使用函数setState(prevState => ({ ...prevState, stat: "loading" }));
返回函数 大概率 用 callback,对象用 useMemo
六、redux
useState 适合定义单个状态,useReducer适合定义一群会互相影响的状态。
redux-thunk 及其原理
immer 纯纯的神器
七、react-query
其他
git 关联远程地址git remote add origin https://XX
height: 100vh; vh 是视口高度,整个视口被分为100份
decodeURIComponent 和 encodeURIComponent decodeURI
users.find(user => user.id === project.personId)?.name ?的作用是如果前面find的结果是undefined,那么整个表达式的值都是undefined,不会报错。
怎样更改端口号
在.env.development中添加PORT=3002
限制object为键值对的对象类型
object: { [key: string]: unknown }
useEffect 或 useCallback 的依赖列表中可以省略 setState
React官网:React 会确保 setState 函数的标识是稳定的,并且不会在组件重新渲染时发生变化。这就是为什么可以安全地从 useEffect 或 useCallback 的依赖列表中省略 setState 。