React Hooks 你不來了解下?

前言

最近在看 React 的新語法—— React Hooks,只能一句話歸納:React 語法真的是愈來愈強大,越寫代碼越少。html

強烈推薦還沒看 React Hooks 的同窗去學習下,這會讓你寫react 項目變得很是爽!react

之前 React 組件能夠當作是: 無狀態組件(function定義)和有狀態組件(class 定義),React Hooks 出現以後,咱們基本全部的組件均可以用function定義,包括有組態組件,基本廢除了 寫 class 語法的 複雜性,讓咱們寫代碼真正變成了函數式編程。git

狀態 Hooks(useState)

這裏說明一點,react中全部的Hooks都是一種函數,函數都是用來實現特定功能的。github

useState 提供了建立組件state的功能,用法:web

const [count, setCount] = useState(0)
複製代碼

useState() 接受惟一一個狀態初始值參數,返回包含狀態和改變狀態對應的函數的數組,這裏採用 數組解構方法得到 狀態變量 count ,改變狀態方法 setCount。ajax

強調一點:編程

useState() 傳入的初始值不必定非要是個對象,能夠爲普通數據類型,好比:Number,String等,初始值用做組件初次渲染。redux

setCount() 接受一個全新的state狀態,react會直接所有替換掉原來的state狀態,這點和 setState() 有所不一樣。數組

Example:瀏覽器

import React, { 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>
  )
}
export default Example;

複製代碼

當把 Example 組件渲染到頁面上時,能夠經過點擊按鈕實時改變 count 狀態,react 也會根據狀態改變從新渲染頁面。

執行反作用操做Hooks(useEffect)

以前在寫 react 組件時,每每咱們在組件的生命週期函數裏面作一些額外的操做,好比發送ajax請求獲取數據,清除定時器和異步執行任務等。

咱們發現這樣操做重複的代碼不少,並且若是對於 react 生命週期鉤子不熟悉的話,很容易出錯,因而乎,useEffect() Hooks 出現了。

useEffect() hooks 能夠容許咱們在 react 函數式組件中執行一些額外的反作用操做。

在 React 組件中有兩種常見反作用操做:

  • 須要清除的,好比開啓的定時器,訂閱外部數據源等,這些操做若是在組件消亡後不及時清除會致使內存泄漏。

  • 不須要清除的,好比發起網絡請求,手動變動 DOM,記錄日誌等。

在 react 官網中有一段話很重要:

若是你熟悉 React class 的生命週期函數,你能夠把 useEffect Hook 看作 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。

Example:

import React, { useState , useEffect } from 'react';
function Example(){
  const [ count , setCount ] = useState(0);
  useEffect(()=>{
    console.log(`You clicked ${count} times`)
  })
  return (
    <div> <p>You clicked {count} times</p> <button onClick={()=>{setCount(count+1)}}>click me</button> </div>
  )
}
export default Example;
複製代碼

咱們在剛纔的例子上新增了一個功能,每次咱們 count 狀態改變的時候都會在控制檯打印出咱們點擊的次數。能夠看到使用 useEffect() Hooks 輕鬆實現。

強調一點:

  • react首次渲染和以後的每次渲染都會調用一遍useEffect函數,而以前咱們要用兩個生命週期函數分別表示首次渲染(componentDidMonut)和更新致使的從新渲染(componentDidUpdate)。

  • useEffect中定義的函數的執行不會阻礙瀏覽器更新視圖,也就是說這些函數是異步執行的,而componentDidMonut和componentDidUpdate中的代碼都是同步執行的。

  • 能夠爲 useEffect() 傳入第二個參數,它是一個數組,數組裏面表示這個反作用的操做依賴的狀態變量,換句話說:若是這個反作用的操做依賴的狀態變量沒有改變,則不會執行反作用操做。

組件之間傳值Hooks(useContext)

以前在用類聲明組件時,父子組件的傳值是經過組件屬性和props進行的,那如今使用方法(Function)來聲明組件,已經沒有了constructor構造函數也就沒有了props的接收,那父子組件的傳值就成了一個問題。React Hooks 爲咱們準備了useContext。

useContext它能夠幫助咱們跨越組件層級直接傳遞變量,實現共享。

Example:

一:利用 createContext 建立上下文

import React, { useState , createContext } from 'react';

// 建立一個 CountContext
const CountContext = createContext()

function Example(){
  const [ count , setCount ] = useState(0);
  return (
    <div> <p>You clicked {count} times</p> <button onClick={()=>{setCount(count+1)}}>click me</button> {/* 將 context 傳遞給 子組件,context 值由value props決定*/} <CountContext.Provider value={count}> <Counter/> </CountContext.Provider> </div> ) } export default Example; 複製代碼

二:使用useContext 獲取上下文

對於要接收context的後代組件,只需引入 useContext() Hooks 便可。

function Counter(){
  const count = useContext(CountContext)  //一句話就能夠獲得count
  return (<h2>{count}</h2>)
}
複製代碼

強調一點:

useContext 的參數必須是 context 對象自己:

  • 正確: useContext(MyContext)
  • 錯誤: useContext(MyContext.Consumer)
  • 錯誤: useContext(MyContext.Provider)

當組件上層最近的 <MyContext.Provider> 更新時,該 Hook 會觸發重渲染,並使用最新傳遞給 <MyContext.Provider> 的 context value 值。

處理更爲複雜state結構的Hooks(useReducer)

前面咱們使用的 useState Hooks能夠爲組件提供 state和操做改變state狀態的方法,但每每有時候咱們的state 結構更爲複雜,例如 state 包含多個子值,或者下一個 state 依賴於以前的 state 等,這時候 useReducer 比 useState 更合適。

useReducer 的用法:

const [state, dispatch] = useReducer(reducer, initialArg, init);
複製代碼

useReducer 接收一個形如 (state, action) => newState 的 reducer,並返回當前的 state 以及與其配套的 dispatch 方法。

咱們可使用 useReducer 來從新寫咱們開篇計數器的demo:

Example:

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      return state
  }
}
// 定義 Counter 組件
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div> Count: {state.count} <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </div>
  );
}

複製代碼

對於熟悉 redux 的夥伴,這種寫法很容易就能理解,每次經過點擊按鈕分發一個action來更新state狀態數據。

強調一點:

React 不使用 state = initialState 這一由 Redux 推廣開來的參數約定。有時候初始值依賴於 props,所以須要在調用 Hook 時指定。若是你特別喜歡上述的參數約定,能夠經過調用 useReducer(reducer, undefined, reducer) 來模擬 Redux 的行爲,但咱們不鼓勵你這麼作。

這裏是 react 官網提供的一句話,也就是說咱們爲 state 提供初始值的時候不可以像 redux 中利用 ES6 默認參數來指定,必須得經過 useReducer來指定。這個切記,由於我也踩過坑!

對於 useReducer 和 useState的區別主要是如下兩點:

  • 當 state 狀態值結構比較複雜時,使用 useReducer 更有優點。
  • 使用 useState 獲取的 setState 方法更新數據時是異步的;而使用 useReducer 獲取的 dispatch 方法更新數據是同步的。

性能優化Hooks(useMemo)

咱們知道 使用class 形式的組件有着生命週期 shouldCompnentUpdate函數用來優化組件的性能,防止每次渲染形成昂貴的開銷。 使用function的形式來聲明組件,失去了shouldCompnentUpdate(在組件更新以前)這個生命週期,也就是說咱們沒有辦法經過組件更新前條件來決定組件是否更新。並且在函數組件中,也再也不區分mount和update兩個狀態,這意味着函數組件的每一次調用都會執行內部的全部邏輯,就帶來了很是大的性能損耗。

useMemo主要用來解決使用React hooks產生的無用渲染的性能問題。

舉個例子:

在 A 組件中有兩個子組件 B 和 C,當 A 組件中傳給 B 的 props 發生變化時,A 組件狀態會改變,從新渲染。此時 B 和 C 也都會從新渲染。其實這種狀況是比較浪費資源的,如今咱們就可使用 useMemo 進行優化,B 組件用到的 props 變化時,只有 B 發生改變,而 C 卻不會從新渲染。

Example:

// A 組件

import React from 'react';

export default ({ text }) => {
  console.log('component A:', 'render');
  return <div> A 組件:{ text }</div>
}

// B 組件

import React from 'react';

export default ({ text }) => {
  console.log('component B:', 'render');
  return <div> B 組件:{ text }</div>
}

// App 組件

import React, { useState, useMemo } from 'react';
import A from './ExampleA';
import B from './ExampleB';

export default () => {
  const [a, setA] = useState('A');
  const [b, setB] = useState('B');

  const exampleA = useMemo(() => <A text={a} />, [a]);
  const exampleB = useMemo(() => <B text={b} />, [b]);

  return (
    <div> { exampleA } { exampleB } <br /> <button onClick={ () => setA('A改變了') }>修改傳給 A 的屬性</button> &nbsp;&nbsp;&nbsp;&nbsp; <button onClick={ () => setB('B改變了') }>修改傳給 B 的屬性</button> </div>
  )
}
複製代碼

咱們點擊不一樣的按鈕,控制檯都只會打印一條輸出,改變 a 或者 b,A 和 B 組件都只有一個會從新渲染。

其實 React 還有不少經常使用的 Hooks,好比用來獲取DOM元素 和保存變量的useRef,優化函數式組件性能的useCallback等,對於這些的Hooks你們能夠去react 官網瞭解下,相信你們應該也能搞清楚,這裏就不作進一步探討。

React Hooks Api

結語

本篇文章出自於咱們 web-study 倉庫,若是喜歡的話,歡迎給個 star!

地址: web-study

相關文章
相關標籤/搜索