1、react两大类
class与function(函数)
2、 react函数类与普通函数区别
react函数类承接ui渲染与更新ui
3、 react通信方式有那五类
props和callback、context(跨层)、event事件、ref传递、状态管理
4、 react父传子、子传父
props和callback
父传子props
import {useState} from 'react';
const Father =()=>{
const [flag, setFlag]= useState<boolen>(true);
return(
<>
<div>我是父组件</div>
<Son son='父组件'/>
</>)
}
const Son = (props:{son:string})=>{
const {son} = props
return (<div>{son}</div>);
}
export default Father
子传父callback通知
import {useState} from 'react';
import { Button } from 'antd';
Father =()=>{
const [number, setNumber]= useState<number>(1);
return(
<>
<div>我是父组件</div>
<Son son={(number)=>{
setNumber();
}}/>
</>)
}
const Son = ({son})=>{
const [number, setNumber] = useState<number>(0)
return (<div>我是子组件</div>
<button onClik={()=>{
const res = number+1
setNumber(res)
son(res)
}}></button>
);
}
export default Father
5、 跨层传递context方式
创建context方法:React.createContext();
外界数据链接通过Provider
消费层Consumer
import {useState, Component} from 'react';
import {Btton Checkbox, Input } from 'antd';
//创建context方法
const TheContext = React.createContext();
//主体颜色
const theme = {
dark: {
color: "#5B8FF9",
background: "#5B8FF9",
border: "1px solid #5B8FF9",
type: "dark",
buttomType: "primary",
},
light: {
color: "#E86452",
background: "#E86452",
border: "1px solid #E86452",
type: "light",
buttomType: "default",
},
};
const Father =()=>{
const [theContext, setTheContext] = useState(theme.dark);
return (
<ThemeContext.Provider
value={{ ...themeContextValue, setTheme: setThemeContext }}
>
<Child />
</ThemeContext.Provider>
);
}
class Child extends Component<any, any> {
static contextType = ThemeContext;
render() {
const { border, setTheme, color, background, buttomType }: any =
this.context;
return (
<div style={{ border, color, padding: 20 }}>
<div>
<span> 选择主题: </span>
<CheckboxView
label="主题1"
name="dark"
onChange={() => setTheme(theme.dark)}
/>
<CheckboxView
label="主题2"
name="light"
onChange={() => setTheme(theme.light)}
/>
</div>
<div style={{ color, marginTop: 8 }}>
大家好,我是小杜杜,一起玩转Hooks吧!
</div>
<div style={{ marginTop: 8 }}>
<Input
placeholder="请输入你的名字"
style={{ color, border, marginBottom: 10 }}
/>
<Button type={buttomType}>提交</Button>
</div>
</div>
);
}
}
class CheckboxView extends Component<any, any> {
static contextType = ThemeContext;
render() {
const { label, name, onChange } = this.props;
const { color, type }: any = this.context;
return (
<div
style={{
display: "inline-block",
marginLeft: 10,
}}
>
<Checkbox checked={type === name} style={{ color }} onChange={onChange}>
{label}
</Checkbox>
</div>
);
}
}
export default Father;
6、强化组件的几种方式
extends继承、高阶组件模式、自定义Hooks模式,已废弃的mixin模式
7、 extends继承
一般用于class方式中,通过React.Component或者React.PurComponent中,用于强化组件
import React from "react";
import { Button } from "antd";
class Child extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.state = {
msg: "大家好,我是小杜杜,一起玩转Hooks吧!",
};
}
speak() {
console.log("Child中的speak");
}
render() {
return (
<>
<div>{this.state.msg}</div>
<Button type="primary" onClick={() => this.speak()}>
查看控制台
</Button>
</>
);
}
}
class Index extends Child {
speak() {
console.log("extends 模式,强化后会替代Child的speak方法");
}
}
export default Index;
8、 高级组件(HOC)模式
高阶组件类似于extends都是组件强化组件,HOC 可以做很多事情,比如强化 props、条件渲染、性能优化、事件赋能、反向继承等
import React, { useState } from "react";
import { Button } from "antd";
const HOC = (Component: any) => (props: any) => {
return (
<Component
name={"大家好,我是小杜杜,一起玩转Hooks吧!"}
{...props}
></Component>
);
};
const Index: React.FC<any> = (props) => {
const [flag, setFlag] = useState<boolean>(false);
return (
<div>
<Button type="primary" onClick={() => setFlag(true)}>
获取props
</Button>
{flag && <div>{JSON.stringify(props)}</div>}
</div>
);
};
export default HOC(Index);
9、 hooks较class组件优缺点
class三大缺点:繁琐的super强继承逻辑、奇怪this指向不止直接使用箭头函数、繁琐的什么周期(componentDidMount、getDerivedStateFromProps)等九个API
hooks有点:更好的状态复用、友好的替代
10、 hooks的十种API
1、 useState:让hooks的函数组件更新储存数据使用,类似于calss类的this.setState
const [data, setData] = useState('');//可存储数组、object、string、number、function类型
2、 useEffect:数据更新组件,类似于class的componentDidMount组件
import {useState, useEffect} from 'react';
const APP =()=>{
const [number, setNumber] = useState(0);
const [count, setCount] = useState(0);
useEffect(()=>{
console.log('count执行才会执行')
},[count]);//()=>{}destory[]dep
retrun(
<>
<div>
number: {number} count: {count}
</div>
<Button type="primary" onClick={() => setNumber((v) => v + 1)}>
number + 1
</Button>
<Button
type="primary"
style={{ marginLeft: 10 }}
onClick={() => setCount((v) => v + 1)}
>
count + 1
</Button>
</>)
}
useEffect分为destory和dep
destory监听addEventListener 和 removeEventListener
dep决定callback时机,dep不存在会无限制执行
3、 useReducer:是一个升级版的useState
是一个惰性组件,当state和state值相同时,read不更新
const Index: React.FC<any> = () => {
console.log("父组件发生更新");
...
return (
<>
...
<Button
type="primary"
style={{ marginLeft: 10 }}
onClick={() => dispatch({ type: "no", payload: 1 })}
>
无关按钮
</Button>
<Child count={count} />
</>
)
};
const Child: React.FC<any> = ({ count }) => {
console.log("子组件发生更新");
return <div>在子组件的count:{count}</div>;
};
4、 useContext:类似于context组件用于跨层使用
const dataUseContext = userContext(context);//context传入参数
用于组件间的数据共享父、子、孙
import { useState, createContext, useContext } from "react";
import { Button } from "antd";
const CountContext = createContext(-1);
const Child = () => {
const count = useContext(CountContext);
return (
<div style={{ marginTop: 10 }}>
子组件获取到的count: {count}
<Son />
</div>
);
};
const Son = () => {
const count = useContext(CountContext);
return <div style={{ marginTop: 10 }}>孙组件获取到的count: {count}</div>;
};
const Index: React.FC<any> = () => {
let [count, setCount] = useState(0);
return (
<>
<div>父组件中的count:{count}</div>
<Button type="primary" onClick={() => setCount((v) => v + 1)}>
点击+1
</Button>
<CountContext.Provider value={count}>
<Child />
</CountContext.Provider>
</>
);
};
export default Index;
5、 useMemo:减少不要重绘设计
const DatauseMemo = useMemo(fn,deps);
fn函数的返回值会作为缓存志
deps依赖项,数组,会通过数组里面的值来判断进行fn的调整,如果发生变化,则会得到新的缓存
//案例
const usePow = (list: number[]) => {
return useMemo(
() =>
list.map((item: number) => {
console.log(1);
return Math.pow(item, 2);
}),
[]
);
};
6、 useCallback:与useMemo类似,不同点是useMemo返回的是值,useCallback返回的是个函数
const resfn = useCallback(fn,deps);
fn:函数的返回值作为缓存值
deps:依赖项,数组会通过数组来判断fn的调用,如果依赖项变化会改变
resfn :更新之后的数据源,即fn函数,如果deps中历来发生变化,将重新fn,没变化取上一次函数
import { useState, useCallback, memo } from "react";
import { Button } from "antd";
const Index: React.FC<any> = () => {
let [count, setCount] = useState(0);
let [flag, setFlag] = useState(true);
const add = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<>
<TestButton onClick={() => setCount((v) => v + 1)}>普通点击</TestButton>
<TestButton onClick={add}>useCallback点击</TestButton>
<div>数字:{count}</div>
<Button type="primary" onClick={() => setFlag((v) => !v)}>
切换{JSON.stringify(flag)}
</Button>
</>
);
};
const TestButton = memo(({ children, onClick = () => {} }: any) => {
console.log(children);
return (
<Button
type="primary"
onClick={onClick}
style={children === "useCallback点击" ? { marginLeft: 10 } : undefined}
>
{children}
</Button>
);
});
export default Index;
7. useRef:获取当前绑定元素所有属性
const ref= useRef(initVlaue);
initVlaue初始值,默认值
ref返回的值在current的对象,需要通过current这个对象获取
import React from "react";
import { useRef, useState } from "react";
const TestDemo =()=>{
const scrollRef = useRef(null);
const [clientHeight, setClientHeight] = useState(0);
const [scrollTop, setScrollTop] = useState(0);
const [scrollHeight, setScrollHeight] = useState(0);
const onScroll = () => {
if (scrollRef?.current) {
let clientHeight = scrollRef?.current.clientHeight; //可视区域高度
let scrollTop = scrollRef?.current.scrollTop; //滚动条滚动高度
let scrollHeight = scrollRef?.current.scrollHeight; //滚动内容高度
setClientHeight(clientHeight);
setScrollTop(scrollTop);
setScrollHeight(scrollHeight);
}
};
return (
<>
<div>
<p>可视区域高度:{clientHeight}</p>
<p>滚动条滚动高度:{scrollTop}</p>
<p>滚动内容高度:{scrollHeight}</p>
</div>
<div
style={{ height: 200, border: "1px solid #000", overflowY: "auto" }}
ref={scrollRef}
onScroll={onScroll}
>
<div style={{ height: 2000 }}></div>
</div>
</>
);
};
export default TestDemo;
8、useImperativeHandle:可以通过 ref 或 forwardRef 暴露给父组件的实例值,所谓的实例值是指值和函数
useImperativeHandle(ref,createHandle, deps)
ref:接受 useRef 或 forwardRef 传递过来的 ref;
createHandle:处理函数,返回值作为暴露给父组件的 ref 对象;
deps:依赖项,依赖项如果更改,会形成新的 ref 对象
import React from "react";
import { useRef, useState, useImperativeHandle } from "react";
const Son = ({cRef}) => {
const [count, setCount] = useState(0)
useImperativeHandle(cRef, () => ({
add
}))
const add = () => {
setCount((v) => v + 1)
}
return <div>
<p>点击次数:{count}</p>
<button onClick={() => add()}> 子组件的按钮,点击+1</button>
</div>
}
const TestDemo =()=>{
const scrollRef = useRef(null);
return (
<>
<div>
我是父组件
<button
type="primary"
onClick={() => scrollRef.current.add()}
>
父组件上的按钮,点击+1
</button>
</div>
<Son cRef={scrollRef}/>
</>
);
};
export default TestDemo;
9、 useLayoutEffect与useEffect方法一样,useLayoutEffect它是同步执行
import { useState, useEffect, useLayoutEffect } from "react";
const Index: React.FC<any> = () => {
let [count, setCount] = useState(0);
let [count1, setCount1] = useState(0);
useEffect(() => {
if(count === 0){
setCount(10 + Math.random() * 100)
}
}, [count])
useLayoutEffect(() => {
if(count1 === 0){
setCount1(10 + Math.random() * 100)
}
}, [count1])
return (
<>
<div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
<div>useEffect的count:{count}</div>
<div>useLayoutEffect的count:{count1}</div>
</>
);
};
export default Index;
之所有用的少是因为同步执行容易阻塞线程,userEffect是异步不会有这种问题
10、useDebugValue用于检查自定义hooks
11、 hooks常用的五种API
1、useSynExternalStore用户强制状态更新,使得外部store支持并发读取
const state = useSynExternalStore(
subscribe,
getSnapshot,
getServerSnapshot
)
subscribe:订阅函数,用于注册一个回调函数,当存储值发生更改时被调用。 此外,useSyncExternalStore 会通过带有记忆性的 getSnapshot 来判断数据是否发生变化,如果发生变化,那么会强制更新数据;
getSnapshot:返回当前存储值的函数。必须返回缓存的值。如果 getSnapshot 连续多次调用,则必须返回相同的确切值,除非中间有存储值更新;
getServerSnapshot:返回服务端(hydration 模式下)渲染期间使用的存储值的函数
import { useSyncExternalStore } from "react";
import { Button } from "antd";
import { combineReducers, createStore } from "redux";
const reducer = (state: number = 1, action: any) => {
switch (action.type) {
case "ADD":
return state + 1;
case "DEL":
return state - 1;
default:
return state;
}
};
/* 注册reducer,并创建store */
const rootReducer = combineReducers({ count: reducer });
const store = createStore(rootReducer, { count: 1 });
const Index: React.FC<any> = () => {
//订阅
const state = useSyncExternalStore(
store.subscribe,
() => store.getState().count
);
return (
<>
<div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
<div>数据源: {state}</div>
<Button type="primary" onClick={() => store.dispatch({ type: "ADD" })}>
加1
</Button>
<Button
style={{ marginLeft: 8 }}
onClick={() => store.dispatch({ type: "DEL" })}
>
减1
</Button>
</>
);
};
export default Index;
2、 useTransition:返回一个状态值标识过渡更新任务等待
const [isPending, startTransition] = useTransition();
isPending:布尔值,过渡状态的标志,为 true 时表示等待状态;
startTransition:可以将里面的任务变成过渡更新任务。
import { useState, useTransition } from "react";
import { Input } from "antd";
const Index: React.FC<any> = () => {
const [isPending, startTransition] = useTransition();
const [input, setInput] = useState("");
const [list, setList] = useState<string[]>([]);
return (
<>
<div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
<Input
value={input}
onChange={(e) => {
setInput(e.target.value);
startTransition(() => {
const res: string[] = [];
for (let i = 0; i < 10000; i++) {
res.push(e.target.value);
}
setList(res);
});
}}
/>
{isPending ? (
<div>加载中...</div>
) : (
list.map((item, index) => <div key={index}>{item}</div>)
)}
</>
);
};
export default Index;
3. useDeferredValue可以让状态滞后派生,与 useTransition 功能类似,推迟屏幕优先级不高的部分
const deFerredValue = useDeferredValue(value);
value:接受一个可变的值,如useState所创建的值。
deferredValue:返回一个延迟状态的值。
import { useState, useDeferredValue } from "react";
import { Input } from "antd";
const getList = (key: any) => {
const arr = [];
for (let i = 0; i < 10000; i++) {
if (String(i).includes(key)) {
arr.push(<li key={i}>{i}</li>);
}
}
return arr;
};
const Index: React.FC<any> = () => {
//订阅
const [input, setInput] = useState("");
const deferredValue = useDeferredValue(input);
console.log("value:", input);
console.log("deferredValue:", deferredValue);
return (
<>
<div>大家好,我是小杜杜,一起玩转Hooks吧!</div>
<Input value={input} onChange={(e: any) => setInput(e.target.value)} />
<div>
<ul>{deferredValue ? getList(deferredValue) : null}</ul>
</div>
</>
);
};
export default Index;
4、 useInsertionEffect: 与 useEffect 一样,但它在所有 DOM 突变之前同步触发
import { useInsertionEffect } from "react";
const Index: React.FC<any> = () => {
useInsertionEffect(() => {
const style = document.createElement("style");
style.innerHTML = `
.css-in-js{
color: blue;
}
`;
document.head.appendChild(style);
}, []);
return (
<div>
<div className="css-in-js">大家好,我是小杜杜,一起玩转Hooks吧!</div>
</div>
);
};
export default Index;
5、 useId用于创建唯一ID
const id = useId();
12、 响应式开发useState
hooks通过改变数据源来推动整个数据改变状态,常用的useState、useReducer
const [name,setName] =useState(xxx);
useLatest
useLatest:值永远是最新值,可以避免闭包问题
const ref = useLatest(count);
useEffect(() => {
const interval = setInterval(() => {
console.log("count:", count);
console.log("ref:", ref);
setCount(ref.current + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
useMount和useUnmout
useMount只在组件初始化化执行hook
useUnmount只在组件卸载时hook
import { useState } from "react";
import { useMount, useUnmount } from "../../hooks";
import { Button, message } from "antd";
const Child = () => {
useMount(() => {
message.info("首次渲染");
});
useUnmount(() => {
message.info("组件已卸载");
});
return <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>;
};
const Index = () => {
const [flag, setFlag] = useState<boolean>(false);
return (
<div>
<Button type="primary" onClick={() => setFlag((v) => !v)}>
切换 {flag ? "unmount" : "mount"}
</Button>
{flag && <Child />}
</div>
);
};
export default Index;
useUnmountedRef
获取当前组件是否卸载,这个钩子的思路也很简单,只需要利用 useEffect 的状态,来保存对应的值就 ok 了
import { useState } from "react";
import { useUnmountedRef, useUnmount, useMount } from "../../hooks";
import { Button } from "antd";
const Child = () => {
const unmountedRef = useUnmountedRef();
useMount(() => {
console.log("初始化:", unmountedRef);
});
useUnmount(() => {
console.log("卸载:", unmountedRef);
});
return <div>大家好,我是小杜杜,一起玩转Hooks吧!</div>;
};
const Index = () => {
const [flag, setFlag] = useState<boolean>(false);
return (
<div>
<Button type="primary" onClick={() => setFlag((v) => !v)}>
切换 {flag ? "卸载" : "初始化"}
</Button>
{flag && <Child />}
</div>
);
};
export default Index;
useSafeState
useSafeState:使用方法与 useState 的用法完全一致,但在组件卸载后异步回调内的 setState 不再执行,这样可以避免因组件卸载后更新状态而导致的内存泄漏。
import { useCallback, useState } from "react";
import type { Dispatch, SetStateAction } from "react";
import useUnmountedRef from "../useUnmountedRef";
function useSafeState<S>(
initialState: S | (() => S)
): [S, Dispatch<SetStateAction<S>>];
function useSafeState<S = undefined>(): [
S | undefined,
Dispatch<SetStateAction<S | undefined>>
];
function useSafeState<S>(initialState?: S | (() => S)) {
const unmountedRef: { current: boolean } = useUnmountedRef();
const [state, setState] = useState(initialState);
const setCurrentState = useCallback((currentState: any) => {
if (unmountedRef.current) return;
setState(currentState);
}, []);
return [state, setCurrentState] as const;
}
export default useSafeState;
useUpdata
强制组件重新渲染,最终返回一个函数。
import { useUpdate } from "../../hooks";
import { Button, message } from "antd";
const Index = () => {
const update = useUpdate();
return (
<div>
<div>时间:{Date.now()}</div>
<Button
type="primary"
onClick={() => {
update();
}}
>
更新
</Button>
</div>
);
};
export default Index;
useCreation
强化 useMemo 和 useRef,用法与 useMemo 一样,一般用于性能优化
如何增强:
● useMemo 的第一个参数 fn,会缓存对应的值,那么这个值就有可能拿不到最新的值,而 useCreation 拿到的值永远都是最新值;
● useRef 在创建复杂常量的时候,会出现潜在的性能隐患(如:实例化 new Subject),但 useCreation 可以有效地避免。
import React, { useState } from "react";
import { Button } from "antd";
import { useCreation } from "../../hooks";
const Index: React.FC<any> = () => {
const [flag, setFlag] = useState<boolean>(false);
const getNowData = () => {
return Math.random();
};
const nowData = useCreation(() => getNowData(), []);
return (
<>
<div>正常的函数: {getNowData()}</div>
<div>useCreation包裹后的: {nowData}</div>
<Button
type="primary"
onClick={() => {
setFlag((v) => !v);
}}
>
切换状态{JSON.stringify(flag)}
</Button>
</>
);
};
export default Index;
useReactive
一种具备响应式的 useState,用法与 useState 类似,但可以动态地设置值。
import { useReactive } from "../../hooks";
import { Button, Input } from "antd";
const Index = () => {
const state = useReactive<any>({
count: 0,
name: "大家好,我是小杜杜,一起玩转Hooks吧!",
flag: true,
arr: [],
bugs: ["小杜杜", "react", "hook"],
addBug(bug: string) {
this.bugs.push(bug);
},
get bugsCount() {
return this.bugs.length;
},
});
return (
<div>
<div style={{ fontWeight: "bold" }}>基本使用:</div>
<div style={{ marginTop: 8 }}> 对数字进行操作:{state.count}</div>
<div
style={{
margin: "8px 0",
display: "flex",
justifyContent: "flex-start",
}}
>
<Button type="primary" onClick={() => state.count++}>
加1
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => state.count--}
>
减1
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => (state.count = 7)}
>
设置为7
</Button>
</div>
<div style={{ marginTop: 8 }}> 对字符串进行操作:{state.name}</div>
<div
style={{
margin: "8px 0",
display: "flex",
justifyContent: "flex-start",
}}
>
<Button type="primary" onClick={() => (state.name = "小杜杜")}>
设置为小杜杜
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => (state.name = "Domesy")}
>
设置为Domesy
</Button>
</div>
<div style={{ marginTop: 8 }}>
{" "}
对布尔值进行操作:{JSON.stringify(state.flag)}
</div>
<div
style={{
margin: "8px 0",
display: "flex",
justifyContent: "flex-start",
}}
>
<Button type="primary" onClick={() => (state.flag = !state.flag)}>
切换状态
</Button>
</div>
<div style={{ marginTop: 8 }}>
{" "}
对数组进行操作:{JSON.stringify(state.arr)}
</div>
<div
style={{
margin: "8px 0",
display: "flex",
justifyContent: "flex-start",
}}
>
<Button
type="primary"
onClick={() => state.arr.push(Math.floor(Math.random() * 100))}
>
push
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => state.arr.pop()}
>
pop
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => state.arr.shift()}
>
shift
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => state.arr.unshift(Math.floor(Math.random() * 100))}
>
unshift
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => state.arr.reverse()}
>
reverse
</Button>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => state.arr.sort()}
>
sort
</Button>
</div>
<div style={{ fontWeight: "bold", marginTop: 8 }}>计算属性:</div>
<div style={{ marginTop: 8 }}>数量:{state.bugsCount} 个</div>
<div style={{ margin: "8px 0" }}>
<form
onSubmit={(e) => {
state.bug ? state.addBug(state.bug) : state.addBug("domesy");
state.bug = "";
e.preventDefault();
}}
>
<Input
type="text"
value={state.bug}
style={{ width: 200 }}
onChange={(e) => (state.bug = e.target.value)}
/>
<Button type="primary" htmlType="submit" style={{ marginLeft: 8 }}>
增加
</Button>
<Button style={{ marginLeft: 8 }} onClick={() => state.bugs.pop()}>
删除
</Button>
</form>
</div>
<ul>
{state.bugs.map((bug: any, index: number) => (
<li key={index}>{bug}</li>
))}
</ul>
</div>
);
};
export default Index;