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><div>hello 靚仔</div></Child>
}export default CustomInput子組件:import React from "react"const Child = ({children
}) => {return <div>{children}</div>
}export default Child
- 函數用法
父組件:import React from "react"
import Child from './Child'const CustomInput = () => {return <Child>{(arr)=><div>{arr.map((v,idx)=>{return <div key={idx}>{v}</div>})}</div>}</Child>
}export default CustomInput子組件:import React from "react"const Child = ({children
}) => {const arr = [1,2,4,5]return <div>{children(arr)}</div>
}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><div onClick={() => setCount(count + 1)}>add count</div><div onClick={() => setNum(num + 1)}>add num</div><div>count: {count}</div><div>num: {num}</div></div>
}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><div onClick={() => setCount(count + 1)}>add count</div><div onClick={() => setNum(num + 1)}>add num</div><div>count: {count}</div><div>num: {num}</div></div>
}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><div onClick={() => setCount(count + 1)}>add count</div><div onClick={() => setNum(num + 1)}>add num</div><div>count: {count}</div><div>num: {num}</div></div>
}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 servicelogErrorToMyService(error, errorInfo);}render() {if (this.state.hasError) {// You can render any custom fallback UIreturn <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;