react+typescript 学习记录

975 阅读7分钟

杂项:

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] 区别

参考:typescript - Are the types { [key in string]: boolean }; and { [key : string]: boolean }; equivalent? - Stack Overflow

  1. [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
  1. 前者支持可选,后者不支持
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 { namestring }>(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


配置:

  1. 设置绝对路径

在tsconfig.json里添加"baseUrl":"./src",意思是将'./src'设置成绝对路径:

  1. 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前自动进行一次格式化

  1. 将prettier配置在git hook里,让开发团队的人push时都格式化

需要用到工具npx mrm lint-staged (不行的用 npx mrm@2 lint-staged)

安装完后会多出.husky文件夹,git提交前别忘记add git add .husky

package.json里添加ts和tsx类型文件的校验:

  1. eslint相关 prettier和eslint一起工作会发生冲突,所以需要额外工具yarn add eslint-config-prettier -D

在package.json里添加配置,代表用prettier覆盖一部分原来的规则:

  1. 规范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.tsxcontext/index.tsx

四、css in js

antd

安装后在src/index.tsx中导入import 'antd/dist/antd.less';,放在下面,覆盖 jira-dev-tool 已有样式

修改原配置,以主题色为例

安装yarn add @craco/craco,用于修改原有配置

package.jsonreact-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

image.png

利用钩子函数解决:

/**
 * 返回组件的挂载状态,如果还没挂载或者已经卸载,返回false;反之,返回true
 */
export const useMountedRef = () => {
  const mountedRef = useRef(false);

  useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
    };
  });

  return mountedRef;
};

useEffect 缺少依赖的提示

image.png

使用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 。