浅谈React

发布于:2024-07-11 ⋅ 阅读:(14) ⋅ 点赞:(0)

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 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;