react hooks入门

95 阅读5分钟

前言

一个新技术的出现总是有原因的,要么是解决还未被解决的问题,要么是已有的技术有不足之处,需要补足这些补足之处。react hooks一度成为了热门话题,也被广大前端开发者使用。那么它为什么能这么受欢迎呢?原来的类组件有什么问题吗?它怎么使用呢?带着这些问题,我们一起走进react hooks的世界

一、react的类组件存在什么问题

react类组件应该是大家使用的比较多的开发方式,有经验的同学应该有所体会,在项目开发中使用类组件时,你有没有以下这些感觉:

1、 缺少逻辑复用机制

  • 为了复用逻辑增加无实际渲染效果的组件,增加了组件的嵌套层级很深,十分臃肿。
  • 增加了调试的难度,实际运行的效率被降低。

2、 类组件经常会变得很复杂,难以维护。

  • 将一组相互联系的业务逻辑拆分到了多个生命周期函数中,比如componentDidMount里面监听了一个时间,却在componentWillMount里面要取消监听。
  • 在一个生命周期函数里存在多个不想干的业务逻辑,比如componentDidMount会调用很多独立的函数。
  • 类成员方法不能保证this指向的正确性
  • 当我们给一个元素绑定事件,在事件处理函数当中,我们要更改状态的时候,通常需要更正函数内部的this指向,不如this就指向undefined,解决方法就是 bind 或者函数嵌套函数的方式去更改this指向。

二、react hooks的优点

  • 可以相对方便的提取公用的业务逻辑,自定义hooks。
  • 没有复杂的生命周期,容易理解。
  • 完全可选,百分之百后向兼容。它和类组件是完全兼容的,这又降低了学习成本,也就是说一个项目里面你可以使用类组件,同时也可以使用hooks组件。 其实hooks就是函数,有点像把原来的函数组件进行了加强,使之在函数内部能维护自己的状态,能异步调用接口等。

三、react hooks介绍

hooks 中文意思叫钩子,React Hooks 就是一堆钩子函数,React 就是通过这些不同的钩子函数来对组件进行增强。hooks有以下这些函数:

  • useState()
  • useEffects()
  • useReducer()
  • useRef()
  • useCallback()
  • uesContext()
  • useMemo() 这里我们不会讨论每个钩子函数背后的原理。正如标题所指,我们通过使用这些钩子函数达到入门的目的,帮助我们继续向下深挖学习。

useState

import React, { useState, useEffect, useRef, useContext } from "react";
const LikeButton: React.FC = () => {
    const [like, setLike] = useState(0);
    function handleAlertClick() {
        setTimeout(() => {
            alert('you clicked on ' + like)
        }, 3000)
    }
    return (
        <>
            <button onClick={() => { setLike(like + 1);}}>
                {like}👍
            </button>
            <button onClick={handleAlertClick}> Alert!</button>
        </>
    )
}
export default LikeButton

上面例子中,每次点赞一下,点赞数值就+1。这就是利用useState来维护自己的state。当我们点击了alert按钮,然后疯狂点击点赞按钮,我们会发现无论点赞数变成了多少,alert的值永远是初始值0!这是因为每次渲染useState维护的state是独立的。

useState使用小结

  • 接收唯一的参数即状态初始值,初始值可以是任意数据类型。
  • 返回值为数组。 数组解构得到状态和改变状态值的方法。 方法名约定以set + 状态名的格式。
  • 方法可以多次被调用,用以保存不同状态值。
  • 设置状态的方法是异步的。
  • 每次渲染useState维护的state是独立的。

useReducer

官方文档也有说明,useReducer是另一种让函数组件保存状态的方式,不用强调预先学习。大家如果有redux基础应该很容易理解的。

function App2(props) {
  function reducer (state,action) {
    switch (action.type){
      case 'increment': return state + 1;
    }
  }
  const [count,dispatch] = useReducer(reducer,0);
  return <div>
    <span>{count}</span>

    <button onClick={() => {
      dispatch({type: 'increment'})
    }}> 点击给 count + 1</button>
  </div>
}

useContext

跨组件层级获取数据时简化获取数据的代码,之前类组件如果后代组件想要获取祖先组件的一个属性,需要props属性传递多次,代码冗余。使用useContext可以轻松解决这个问题。 要传递数据的组件用provider包裹,需要获取数据的组件在组件内部使用useContext获取数据使用。

import {createContext,useContext} from 'react';
const countContext = createContext();

const Foo0 = ()=>{
  return <countContext.Consumer>
    {value => {
      return <div>{value}</div>
    }}
  </countContext.Consumer>
}
const Foo = ()=>{
  const count = useContext(countContext);
  return <div>
    {count}
  </div>
}
function App3(props) {

  return <countContext.Provider value={100}>
    <Foo0 />
  </countContext.Provider>
}

useEffect

让函数拥有处理副作用的能力。类似生命周期函数。可以把useEffect看作 componentDidMount, componentDidUpdate , componentWillMount 这三个生命周期函数的组合:

  • useEffect(()=>{}) => componentDidMount,componentDidUpdate
  • useEffect(()=>{},[]) => componentDidMount
  • useEffect(()=>()=>{}) => componentWillMount useEffect 中的参数函数不能是异步函数,因为useEffect要返回一个清理函数。而异步函数返回值为promise
function App4(props) {
    const [count, setCount] = useState(()=>{
      return props.count || 0; // 组件第一次被渲染的时候执行
    });
  
    useEffect(()=>{
      console.log('666')
    })
  
    useEffect(()=>{
      console.log('000')
    },[])
  
    useEffect(()=>{
     return ()=>{
       console.log('component is unmount')
     }
    })
    return <div>
      <span>{count}</span>
  
      <button onClick={() => setCount(count + 1)}> 点击给 count + 1</button>
      <button onClick={() => ReactDom.unmountComponentAtNode(document.getElementById('root'))}> 卸载组件</button>
      
    </div>
  }

useMemo

useMemo可以检测某个值的变化,根据变化值计算新值。 useMemo会缓存计算结果。如果检测值没有发生变化,即使组件重新渲染也不会重新计算。可以避免在每个渲染上进行昂贵的计算。

import React, { useState, useMemo } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  const double = useMemo(() => {
    return count * 2
  }, [count]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Click count {double}</button>
    </div>
  )
}

useRef

获取DOM元素对象。在组件的任意一次渲染中,props和state是相互独立的,类似闭包。如何让不同渲染之间产生联系呢?useRef就可以让所有render都对应同一个引用。而且修改useRef值并不会引发render。

function App(){
  const userName = useRef(null)
  return <div ref={userName} onChange={}> 
    <button onClick={()=>{
    	console.log(userName.current)
    }}> </button>
    </div>
}

四、总结

react hooks提供了很多的hooks,常用的hooks基本上是上面几种,所以还需进一步学习更多的hooks函数,另外上面对个hooks的介绍也很简单,知识入门。比如useEffectf返回函数表示返回一个清理函数,那这个清理函数的执行时机是什么?是先清理上一个effect再执行render,然后再注册新的effect,还是先执行render,再执行清理函数后再注册呢?等等很多细节需要继续深入!