【React 函数式组件知识点】

函数式组件的生命周期

使用 useEffect 钩子函数可以模拟类组件的生命周期

javascript 复制代码
useEffect(() => {    
    // 类似于 componentDidMount    
    window.addEventListener('mousemove', () => {});        

    return () => {        
        // 类似于 componentWillUnMount        
        window.removeEventListener('mousemove', () => {})    }
}, [])

useComponentWillMount 的实现

然而要模拟实现 componentWillMount,我们只能另辟蹊径,这里我们来手写一个 useComponentWillMount hook:

javascript 复制代码
const useComponentWillMount = (cb) => {    
    const willMount = useRef(true)    

    if (willMount.current) cb()    

    willMount.current = false
}

值得注意的是,这并不能完全等同于 componentWillMount,因为存在代码顺序带来的问题,比如:

javascript 复制代码
console.log('111')
useComponentWillMount(() => console.log('222'))
// output:
// 111
// 222

在这里,111 会在 useComponentWillMount 之前执行,而在 class 的 componentWillMount 中,是优先其他代码执行的。

因此,在实际开发中,我们要根据场景和需求,去灵活使用。

钩子函数详解

useEffect

在 React hook 中,useEffect 用来取代 componentDidMount 和 componentDidUpdate。主要作用是当页面渲染后,进行一些副作用操作(比如访问 DOM,请求数据)。

useLayoutEffect

useLayoutEffect 的出现是为了解决 useEffect 的页面闪烁问题。useEffect 是在组件挂载后异步执行的,并且执行事件会更加往后,如果我们在 useEffect 里面改变 state 状态,那么页面会出现闪烁(state 可见性变化导致的)。而 useLayoutEffect 是在渲染之前同步执行的,在这里执行修改 DOM 相关操作,就会避免页面闪烁的情况。

useCallback

useCallback 不是用来解决组件中有过多内部函数导致的性能问题
1.我们要知道,js创建一个函数的成本是非常小的,这点计算对于计算机来说是小case
2.其实使用useCallback会产成额外的性能:对deps的判断
3.其实每次组件重新渲染时,都无所谓避免重新创建内部函数,因为即使useCallback的deps没有变,它也会重新创建内部函数作为useCallback的实参

那么,它的作用到底是什么?useCallback的作用其实是用来避免子组件不必要的reRender:首先,假如我们不使用useCallback,在父组件中创建了一个名为handleClick的事件处理函数,根据需求我们需要把这个handleClick传给子组件,当父组件中的一些state变化后(这些state跟子组件没有关系),父组件会reRender,然后会重新创建名为handleClick函数实例,并传给子组件,这时即使用React.memo把子组件包裹起来,子组件也会重新渲染,因为props已经变化了,但这个渲染是无意义的.

对于这种deps不是经常变化的情况,我们用useCallback和React.memo的方式可以很好地避免子组件无效的reRender。但其实社区中对这个useCallback的使用也有争议,比如子组件中只是渲染了几个div,没有其他的大量计算,而浏览器去重新渲染几个dom的性能损耗其实也是非常小的,我们花了这么大的劲,使用了useCallback和React.memo,换来的收益很小,所以一些人认为就不用useCallback,就让浏览器去重新渲染好了。至于到底用不用,此处不深入讨论,我的建议是当子组件中的dom数量很多,或者有一些大量的计算操作,是可以进行这样的优化的。

useReducer

总的来说,useReducer是useState的复杂版,所有useState的规则,useReducer都适用。当我们需要对一个对象执行不同的操作时,可以用 useReducer,比如查询、重置、切换页码都在操作查询参数,这个时候我们就可以使用 useReducer,传一个 reducer 函数,在不同的操作里面执行 dispatch 函数,从而设置不同的值给一个对象

javascript 复制代码
const initFormData = {
  name: "",
  age: 18,
  ethnicity: "汉族"
}
const reducer = (state, action) => {
  switch (action.type) {
    case 'patch': //更新
      return {...state, ...action.formData} //把旧的数据复制到一个对象,把新的数据复制到一个对象,把两个对象合并
    case "reset": //重置
      return initFormData
    default:
      throw new Error()
  }
}
const App = () => {
  console.log('App执行了一遍')
  const [formData, dispatch] = useReducer(reducer, initFormData)
  const onSubmit = () => {
  }
  const onReset = () => {
    dispatch({type: "reset"})
  }
  return (
    <form onSubmit={onSubmit} onReset={onReset}>
      <div>
        <label >
          姓名
          <input value={formData.name} onChange={e=>dispatch({type:"patch",formData:{name: e.target.value}})}/>
        </label>
      </div>
      <div>
        <label >
          年龄
          <input value={formData.age} onChange={e=>dispatch({type:"patch",formData:{age: e.target.value}})}/>
        </label>
      </div>
      <div>
        <label >
          民族
          <input value={formData.ethnicity} onChange={e=>dispatch({type:"patch",formData:{age: e.target.value}})}/>
        </label>
      </div>
      <div>
        <button type="submit">提交</button>
        <button type="reset">重置</button>
      </div>
      <hr/>
      {JSON.stringify(formData)}
    </form>
  )
}

useMemo

useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。它可以用来做下面的事情:
1、跳过代价昂贵的重新计算

2、跳过组件的重新渲染

3、记忆另一个 Hook 的依赖

4、记忆一个函数
默认情况下,当一个组件重新渲染时,React 会递归地重新渲染它的所有子组件,我们可以使用 useMemo 包裹需要大量计算而产生的依赖项,如果依赖项的props值并么有变化,则我们使用缓存的依赖项,如果子组件只依赖通过计算产生的依赖项,则就可以跳过子组件的重新渲染

相关推荐
大杯咖啡几秒前
localStorage与sessionStorage的区别
前端·javascript
RaidenLiu12 分钟前
告别陷阱:精通Flutter Signals的生命周期、高级API与调试之道
前端·flutter·前端框架
非凡ghost13 分钟前
HWiNFO(专业系统信息检测工具)
前端·javascript·后端
非凡ghost15 分钟前
FireAlpaca(免费数字绘图软件)
前端·javascript·后端
非凡ghost21 分钟前
Sucrose Wallpaper Engine(动态壁纸管理工具)
前端·javascript·后端
拉不动的猪23 分钟前
为什么不建议项目里用延时器作为规定时间内的业务操作
前端·javascript·vue.js
该用户已不存在30 分钟前
Gemini CLI 扩展,把Nano Banana 搬到终端
前端·后端·ai编程
地方地方32 分钟前
前端踩坑记:解决图片与 Div 换行间隙的隐藏元凶
前端·javascript
炒米233334 分钟前
【Array】数组的方法
javascript
小猫由里香37 分钟前
小程序打开文件(文件流、地址链接)封装
前端