React Hooks

擁有了hooks,你再也不需要寫Class了,你的所有組件都將是Function
class版

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

使用hook后

import { useState } from 'react';
function Example() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

組件變成了一個函數(shù),但這個函數(shù)卻有自己的狀態(tài)(count),同時它還可以更新自己的狀態(tài)(setCount)。這個函數(shù)之所以這么了不得,就是因為它注入了一個hook--useState,就是這個hook讓我們的函數(shù)變成了一個有狀態(tài)的函數(shù)
Hooks本質(zhì)上就是一類特殊的函數(shù),它們可以為你的函數(shù)型組件(function component)注入一些特殊的功能

React為什么要搞一個Hooks?

1.想要復(fù)用一個有狀態(tài)的組件太麻煩了!

我們都知道react都核心思想就是,將一個頁面拆成一堆獨立的,可復(fù)用的組件,并且用自上而下的單向數(shù)據(jù)流的形式將這些組件串聯(lián)起來。但假如你在大型的工作項目中用react,你會發(fā)現(xiàn)你的項目中實際上很多react組件冗長且難以復(fù)用。尤其是那些寫成class的組件,它們本身包含了狀態(tài)(state),所以復(fù)用這類組件就變得很麻煩。
之前,官方推薦怎么解決這個問題呢?答案是:渲染屬性(Render Props)和高階組件(Higher-Order Components)。 這兩種模式看上去都挺不錯的,很多庫也運用了這種模式,比如我們常用的React Router。但我們仔細看這兩種模式,會發(fā)現(xiàn)它們會增加我們代碼的層級關(guān)系。

2.生命周期鉤子函數(shù)里的邏輯太亂!

我們通常希望一個函數(shù)只做一件事情,但我們的生命周期鉤子函數(shù)里通常同時做了很多事情。比如我們需要在componentDidMount中發(fā)起ajax請求獲取數(shù)據(jù),綁定一些事件監(jiān)聽等等。同時,有時候我們還需要在componentDidUpdate做一遍同樣的事情。當(dāng)項目變復(fù)雜后,這一塊的代碼也變得不那么直觀。

3.this指向

為了保證this的指向正確,我們要經(jīng)常寫這樣的代碼:this.handleClick = this.handleClick.bind(this),或者是這樣的代碼:<button onClick={() => this.handleClick(e)}>。

什么是State Hooks

useState是react自帶的一個hook函數(shù),它的作用就是用來聲明狀態(tài)變量。useState這個函數(shù)接收的參數(shù)是我們的狀態(tài)初始值(initial state),它返回了一個數(shù)組,這個數(shù)組的第[0]項是當(dāng)前當(dāng)前的狀態(tài)值,第[1]項是可以改變狀態(tài)值的方法函數(shù)。
react規(guī)定我們必須把hooks寫在函數(shù)的最外層,不能寫在ifelse等條件語句當(dāng)中,來確保hooks的執(zhí)行順序一致

let showFruit = true;
function ExampleWithManyStates() {
  const [age, setAge] = useState(42);
  if(showFruit) {
    const [fruit, setFruit] = useState('banana');
    showFruit = false;
  }
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
 }

 //第一次渲染
  useState(42);  //將age初始化為42
  useState('banana');  //將fruit初始化為banana
  useState([{ text: 'Learn Hooks' }]); //...

  //第二次渲染
  useState(42);  //讀取狀態(tài)變量age的值(這時候傳的參數(shù)42直接被忽略)
  // useState('banana');  
  useState([{ text: 'Learn Hooks' }]); //讀取到的卻是狀態(tài)變量fruit的值,導(dǎo)致報錯

注意:每次渲染都會有自己獨立的props和state


什么是Effect Hooks

Effect Hook 可以讓你能夠在 Function 組件中執(zhí)行副作用(side effects):

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // 類似于componentDidMount 和 componentDidUpdate:
  useEffect(() => {
    // 更新文檔的標(biāo)題
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

第一,react首次渲染和之后的每次渲染都會調(diào)用一遍傳給useEffect的函數(shù)。而之前我們要用兩個聲明周期函數(shù)來分別表示首次渲染(componentDidMount),和之后的更新導(dǎo)致的重新渲染(componentDidUpdate)。
第二,useEffect中定義的副作用函數(shù)的執(zhí)行不會阻礙瀏覽器更新視圖,也就是說這些函數(shù)是異步執(zhí)行的,而之前的componentDidMountcomponentDidUpdate中的代碼則是同步執(zhí)行的。這種安排對大多數(shù)副作用說都是合理的,但有的情況除外,比如我們有時候需要先根據(jù)DOM計算出某個元素的尺寸再重新渲染,這時候我們希望這次重新渲染是同步發(fā)生的,也就是說它會在瀏覽器真的去繪制這個頁面前發(fā)生。

怎么跳過一些不必要的副作用函數(shù)

我們只需要給useEffect傳第二個參數(shù)即可。用第二個參數(shù)來告訴react只有當(dāng)這個參數(shù)的值發(fā)生改變時,才執(zhí)行我們傳的副作用函數(shù)(第一個參數(shù))

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 只有當(dāng)count的值發(fā)生變化時,才會重新執(zhí)行`document.title`這一句

當(dāng)我們第二個參數(shù)傳一個空數(shù)組[]時,其實就相當(dāng)于只在首次渲染的時候執(zhí)行。也就是componentDidMount加componentWillUnmount的模式。不過這種用法可能帶來bug

模擬場景

componentDidMount、componentDidUpdate
import React,{useEffect} from 'react'
useEffect(()=>{
     //每次componentDidMount、componentDidUpdate都會調(diào)用這兒
})

componentDidMount 加第二個參數(shù)[]

useEffect(()=>{
     //componentDidMount執(zhí)行
},[])

componentDidUnMount 加第二個參數(shù)[]

useEffect(()=>{
     //componentDidMount執(zhí)行
     return ()=>{
     //componentDidUnMount執(zhí)行
     }
},[])

React.memo和useMemo 減少不必要的渲染

React.momo其實并不是一個hook,它其實等價于PureComponent,但是它只會對比props

import React, { useState } from 'react';

export const Count = React.memo((props) => {
  const [data, setData] = useState({
    count: 0,
    age: 18,
  });
  
  const handleClick = () => {
    const { count } = data;
    setData({
      ...data,
      count: count + 1,
    })
  };

  return (<button onClick={handleClick}>count:{data.count}</button>)
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容