为什么学习React17
公司使用的是vue技术栈,并且因为历史原因,以及外部因素,可能不会使用react来做生产环境的项目,最近vue3走上正轨,大家都需要抛弃vue2.x的编码思想,在vue3中,hook思想开始越发明显,所以学习react17也是想借此加深对hook的理解,同时取长补短,综合vue与react的优点,在实际的开发中更好的完成开发任务
useState的疑问
关于react的useState有一个疑问,useState里面是一个对象,我想要修改对象里面的某一个字段,每次都需要...state
,把之前的数据预先填入,这做法感觉有点傻
自定义hook
hook是在特定换下调用自己的代码
实现自定义hook必须使用关键字use
开头,这是ESLint的规则
自定义hook一般用于需要持续监听的值,而针对纯工具函数则不需要进行处理
实现一个hook风格的防抖函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const [param, setParam] = useState('ha ha'); const debounceParam = useDebounce(param, 2000);
useEffect(() => { console.log('log log') }, [debounceParam]);
/** * 去抖函数 * @param value 监听的值 * @param delay 防抖时间 * @returns 变化后变量 */ export function useDebounce(value, delay) { // 声明一个保存防抖结果的字段 const [debounceValue, setDebounceValue] = useState(value); useEffect(() => { // 每次value,delay发生变化的时候生成一个定时器 const timeout = setTimeout(() => { setDebounceValue(value); }, delay); // 在执行下一个useEffect之前,会执行上一个useEffect的返回函数 return () => clearTimeout(timeout); }, [value, delay]); return debounceValue; }
|
- useDebounce是一个hook风格的函数
- 每次param发生变化都会触发useDebounce中的useEffect
- 而在2000ms时间内如果重复触发就会执行上一个effect return的函数
- 进而不修改debounceValue,直到2000ms内无任何操作,触发setDebounceValue修改属性,改变了debounceParam
- 第一个useEffect触发,触发打印
log log
react中使用TS做代码静态检查
- 函数类型
fn: () => void
- any类型是危险的,应当尽量不用,但是对于模糊的类型,我们可以在标注类型为unknown,这样就不会对赋值做任何校验,但是ts不允许我们对unknown类型做出任何处理,所以可以吧unknown理解成为加强版any
- 针对参数不固定,返回值不确定的场景,应当使用泛型,在方法名称后面加,然后在参数上指定某一个为T,则T跟这参数的类型而变化
自定义hook useArray
传入一个hook,我们返回针对这个hook的可使用的对应方法,总体来说实现一个hook还是一个比较简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| /** * hook处理的Array * @param value * @returns */ export function useArray<V>(value: V[]) { const [arrayValue, setArrayValue] = useState(value); const clear = () => { setArrayValue([]); }; const add = (value: V) => { setArrayValue([...arrayValue, value]); }; const removeIndex = (index: number) => { let value = [...arrayValue]; value.splice(index, 1); setArrayValue(value); };
return { value: arrayValue, add, clear, removeIndex, }; }
|
关于TS中interface的属性继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| interface a { a: number; }
interface b extends a { b: number; }
let c: b = { a: 1, b: 2, };
function test(p: a) { console.log(p); }
test(c);
|
useContext
useContext主要用于多个组件之间共享状态
在parent组件中存在child1.child2,child3组件,我们希望这些组件之前都共享一个状态1
- 生成createContext
1
| const TestContext = React.createContext({});
|
- 使用createContext.Provider包裹parent组件
1 2 3 4 5 6 7 8 9 10 11
| <TestContext.Provider value={{ username: '我是变量', }} > <div className="parent"> <Child1 /> <Child2 /> <Child3 /> </div> <TestContext.Provider/>
|
- 在子组件中就可以使用useContext来获取父级创建的TestContext
1 2 3 4 5 6 7 8
| const Child1 = () => { const { username } = useContext(TestContext); return ( <div className="child1"> <p>1 message for {username}</p> </div> ) }
|
通过useContext就可以做到子组件共享一个状态,如果状态被改变,所有使用变量的都放都会发生变化
TS类型的一些知识
Parameters
获取函数的全部参数,并且以元祖类型进行返回,Utiltity Types
1 2 3 4 5 6
| function test(a: string, b: number, c: number[], d: any) {}
function testPlus(...[a, b, c, d]: Parameters<typeof test>) { console.log(a, b, c, d); }
|
联合类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| let a:string | number;
type aType:string | number; let a:aType
interface aaa { a:string }
type aaa = { a:string }
|
js的typeof与ts的typeof
Partial与Omit关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type Preson = { name: string; age: String; sex: string; };
const xiaoMin: Partial<Preson> = { age: "非必填", name: "非必填", sex: "非必填", };
const shenMiRen: Omit<Preson, "age" | "name"> = { sex: "男" };
|
关于css一些不知道的
关于rem
em表示相对于父级的font-size
rem表示相对于根元素html的font-size
正常默认font-size为16px,那么1rem === 16px
如果希望rem的比例自定义只需要将html的font-size设置为对应的百分就行,例如设置为62.5的时候1rem === 10px
关于vh
vh的全称为viewport height,100vh就是代表视口的高度
emotion
emotion是一个css in js的方案,也就是使用js来写css代码,这样的话就可以在css中直接写逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13
| import styled from "@emotion/styled";
export const Row = styled.div<{ gap: number; }>` display: flex; align-items: center; > * { margin-top: 0 !important; margin-bottom: 0 !important; margin-right: ${(props) => `${props.gap}rem`}; } `;
|
自定义hook useAsync的实现
useAsync可以理解为针对请求体再次封装,经过封装之后的请求体,自带请求进度,以及针对catch更加优雅的处理方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| import { useState } from "react";
interface State<D> { error: Error | null; data: D | null; status: "idle" | "loading" | "error" | "success"; }
const defaultState: State<null> = { data: null, error: null, status: "idle", };
export const useAsync = <D>(initState?: State<D>) => { const [state, setState] = useState<State<D>>({ ...defaultState, ...initState, }); const setData = (data: D) => { setState({ data, status: "success", error: null, }); }; const setError = (error: Error) => { setState({ data: null, error: error, status: "error", }); }; const setLoading = () => { setState({ data: null, error: null, status: "loading", }); }; const run = (promise: Promise<D>) => { if (!promise || !promise.then) { throw new Error("请传入 promise 类型数据"); } setLoading(); return promise .then((data) => { setData(data); }) .catch((err) => { setError(err); }); }; return { isIdle: state.status == "idle", isLoading: state.status == "loading", isError: state.status == "error", isSuccess: state.status == "success", run, setData, setLoading, setError, ...state, }; };
|
使用阶段
1 2 3 4 5 6 7 8
| const projectData = useAsync<any[]>();
projectData.run(request);
let { data, isLoading,isError,...other} = projectData
|
react中实现捕捉边界错误
关于错误边界的概念在react官网中是这样描述的,组件内的JavaScript错误会导致React的内部状态被破坏,并且在下一次的渲染时会产生可能无法追踪的错误,但是部分ui的JavaScript错误不应该导致整个应用的崩溃,所以react16中引入了错误边界的概念,
错误边界是一种react组件,最终组件可以捕获发生在其朱组件树任何位置的JavaScript错误,并打印错误,同时展示降级ui,而并不会渲染发生崩溃的子组件树,错误边界在渲染期间,生命周期方法和整个组件数的构造函数中捕获错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import React, { ReactNode } from "react";
type FallBackRender = (props: { error: Error | null }) => React.ReactElement;
type Components1 = { children: ReactNode; fallbackRender: FallBackRender }; type Components2 = React.PropsWithChildren<{ fallbackRender: FallBackRender }>;
export class ErrorBoundary extends React.Component< Components2, { error: Error | null } > { state = { error: null, }; static getDerivedStateFromError(error: Error) { return { error }; } render() { const { error } = this.state; const { fallbackRender, children } = this.props; if (error) { console.log("错误执行", error); return fallbackRender(error); } else { return children; } } }
|
使用App.tsx
1 2 3 4 5 6 7 8 9
| <ErrorBoundary fallbackRender={fallPageErrorFallback}> {user ? <AuthenicatedApp /> : <UnAuthenicated />} </ErrorBoundary>
export const fallPageErrorFallback = ({ error }: { error: Error | null }) => ( <p>请求失败了,错误信息{error}</p> );
|
阶段性结束
关于react hook的理念学习已经结束了,这段时间学习来看,确实react在很多方面更加易于理解,更加工程化,同时也学会了很多ts的知识,一直以来ts在我手中都是anyscript,本次学习让我对ts的运用更加熟练;在这个过程中也学会了很多hook相关的理念以及实例,并且可以带着这思想去优化项目代码
可惜我是一名vuer,继续学习react的实际编码,意义已经不是很大,所以关于这个课程的学习,本次告一段落;通过本次学习,非常深刻的体会到了hook的优雅,后面会深入学习vue3,在vue3中将hook与业务结合,让代码更加健壮