浅谈React
forwardRef和useImperativeHandle的联动使用
import React, { useImperativeHandle, useRef } from "react"
import { forwardRef } from "react"
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef<HTMLInputElement>(null)
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current?.focus()
}
}))
return <div>
<input ref={inputRef} />
</div>
})
export default CustomInput
巧用children
一般用法
父组件:
import React from "react"
import Child from './Child'
const CustomInput = () => {
return <Child>
hello 靓仔
</Child>
}
export default CustomInput
子组件:
import React from "react"
const Child = ({
children
}) => {
return
{children}
}
export default Child
函数用法
父组件:
import React from "react"
import Child from './Child'
const CustomInput = () => {
return <Child>
{(arr)=>
{arr.map((v,idx)=>{
return
{v}
})}
}
</Child>
}
export default CustomInput
子组件:
import React from "react"
const Child = ({
children
}) => {
const arr = [1,2,4,5]
return
{children(arr)}
}
export default Child
useEffect
没有依赖,类似于componentDidMount和componentDidUpdate
import React, { useEffect, useState } from "react"
const Detail = () => {
const [count, setCount] = useState(1)
const [num,setNum] = useState(2)
useEffect(()=>{
console.log(count,'count',num,'num')
})
return
<div onClick={() => setCount(count + 1)}>add count
<div onClick={() => setNum(num + 1)}>add num
count: {count}
num: {num}
}
export default Detail
依赖是个空数组,相当于componentDidMount
import React, { useEffect, useState } from "react"
const Detail = () => {
const [count, setCount] = useState(1)
const [num,setNum] = useState(2)
useEffect(()=>{
console.log(count,'count',num,'num')
},[])
return
<div onClick={() => setCount(count + 1)}>add count
<div onClick={() => setNum(num + 1)}>add num
count: {count}
num: {num}
}
export default Detail
有依赖,componentDidMount和对应依赖的componentDidUpdate
import React, { useEffect, useState } from "react"
const Detail = () => {
const [count, setCount] = useState(1)
const [num,setNum] = useState(2)
useEffect(()=>{
console.log(count,'count',num,'num')
},[num])
return
<div onClick={() => setCount(count + 1)}>add count
<div onClick={() => setNum(num + 1)}>add num
count: {count}
num: {num}
}
export default Detail
useEffect和useLayoutEffect
useEffect在渲染后执行,而useLayouEffect是在渲染之前执行
最典型的例子就是实现一个tooltip组件,在性能比较差的情况下,useEffect会先渲染初始状态再更新,而useLayoutEffect会阻塞UI的更新即不会出现组件闪烁的情况~
阻塞代码:
let now = performance.now();
while (performance.now() - now < 100) {
}
useEffect在性能差的情况下会出现以下效果
useContext
依赖注入
父级:
export const ThemeContext = createContext({});
const App = ()=>{
return <ThemeContext.Provider value={{name:"real hot"}}>
......
</ThemeContext.Provider>
}
子级:
const context = useContext(ThemeContext)
console.log(context)
useState的变动
在react管辖下(react17.x.x)
状态能够更改多次,只渲染一次
const handleCount= ()=>{
setCount(count=>count+1)
setCount(count=>count+1)
}
多次更改状态会被合并成一次更改,即一次生效其他无效,只渲染一次
const handleCount= ()=>{
setCount(count+1)
setCount(count+1)
}
在异步任务/原生事件下(react17.x.x)
const handleCount= ()=>{
setTimeout(()=>{
setCount(count=>count+1)
setCount(count=>count+1)
})
}
版本的演变(React18)
react18之后在异步操作或者react事件中都是批量更新,即多个状态更新合成一次渲染,若需要多次渲染可使用flushsync
const handleCount= ()=>{
flushSync(()=>{
setCount(count=>count+1)
})
flushSync(()=>{
setCount(count=>count+1)
})
}
探索生命周期函数
父子组件生命周期执行顺序
挂载:
更新:
卸载:
错误处理
生命周期方法:
getDerivedStateFromError
componentDidCatch
缺点(没办法捕获):
异步操作
事件处理函数报错
错误边界自己报错
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
实现一个简单的Message
import ReactDOM from 'react-dom';
import { Modal } from 'antd';
const ToastFn = () => {
let parent = null;
return {
open: function ({ el, container = document.body }) {
// this.destroy();
parent = document.createElement('div');
document.body.appendChild(parent);
ReactDOM.render(<Modal open onCancel={this.destroy}>{el}</Modal>, parent)
},
destroy: function () {
ReactDOM.unmountComponentAtNode(parent);
},
};
};
const Toast = ToastFn();
export default Toast;