React-Hooks帶來了什么?

對應的代碼倉庫:https://codesandbox.io/s/learning-react-hooks-7ssri

其實對應的React-Hooks帶來的東西很多,其中有什么我們簡單講解一下:

  • 使用函數(shù)的形式代替原來的繼承類的形式!
  • 使用預函數(shù)的形式管理對應的state!
  • 可以使用React-Hooks來創(chuàng)建帶狀態(tài)的組件而并非使用類!

那么對應的我們使用簡單的代碼實例學習React-Hooks! 本文中使用的是CodeSandbox而并非使用 create-react-app創(chuàng)建對應的React項目!

對應的 src/index.js代碼如下所示:

import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<App/>,document.querySelector("#root"));

01|傳統(tǒng)的類形式的組件與React-Hooks組件進行對比

import React,{Component} from "react";

class Example extends Component{
    constructor(props){
        super(props);
        this.state = {count:0};
        this.addCount = this.addCount.bind(this);
    }
    addCount(){
        this.setState({count:this.state.count+1})
    }
    render(){
        return (
            <div>
                <span>You clicked {this.state.count} times</span>
                <button onClick={this.addCount}>Click me</button>
            </div>
        );
    }
}
export default Example;

其實上面的代碼很清楚的就解釋了一個最基本的組件是如何的!

下面看看最基本的React-Hooks的組件是如何的!

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

其實從對應的單詞Hooks中我們就可以知道本身就是鉤子的意思! 為的就是讓你不在寫Class,而是使用function更好的寫React!

其中引入的useState其實就是所謂的狀態(tài)使用,對應的count表示對應的狀態(tài)setCount很明顯就是可以改變狀態(tài)的方法而使用useState(0)表示當前的count值為0

那么如何理解呢? 在對應的代碼中的表示?

  • useState()接收對應的初始值返回一個數(shù)組
  • 數(shù)組的第一位也就是所謂的count表示狀態(tài)變量count
  • 數(shù)組的第二位表示設置狀態(tài)變量的方法setCount

02|React-Hooks的多狀態(tài)聲明

對應的如何使用多狀態(tài)聲明? 上代碼

import React,{useState} from "react";
function Example2(){
    const [age,setAge] = useState(22);
    const [sex,setSex] = useState("男");
    const [work,setWork] = useState("frontEnd");
    return (
        <div>
            <p>ProbeDream:今年{age}歲</p>
            <p>性別:{sex}</p>
            <p>工作:{work}</p>
        </div>
    );
}
export default Example2;

其中別人可能會問,如何確定對應的useState找到自己的state呢? React是根據(jù)useState出現(xiàn)的順序確定的!

import React,{useState} from "react";
let showSex = true;
function Example3(){
    const [age,setAge] = useState(22);
    if(showSex){
        const [sex,setSex] = useState("男");
        showSex = false;
    }
    const [work,setWork] = useState("frontEnd");
    return (
        <div>
            <p>ProbeDream:年齡{age}歲</p>
            <p>性別:{sex}</p>
            <p>工作:{work}</p>
        </div>
    );
}
export default Example3;

如果說你寫下這一行代碼的話,會出現(xiàn)對應的錯誤就是,useState不能夠在ifelse這種條件判斷語句中使用! 不然的話會報錯的!

由此我們可以知道:React Hooks不能出現(xiàn)在條件判斷語句中,因為它必須有完全一樣的渲染順序!

03|useEffect代替常用生命周期函數(shù)

其實在我們寫React的時候使用class制作組件的時候,會經(jīng)常使用到生命周期函數(shù)來處理特定階段的事情!

例如說:

  • Ajax請求后端的數(shù)據(jù)
  • 添加登錄監(jiān)聽和取消登錄
  • 手動修改DOM等等

那么對應的問題來了? 如果說使用React-Hooks的話有沒有對應的生命周期函數(shù)或者說類似于這種功能的方法?

有的,React-Hooks中提供了一種方法叫做useEffect來幫我們解決日常開發(fā)中的問題!

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

function Example03() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(`effect=>You Clicked ${count} times!`);
  });
  return (
    <div>
      <p>You Clicked {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click me!
      </button>
    </div>
  );
}
export default Example03;

通過對應的代碼示例便可以知道對應的效果! 我們梳理一下過程

  • 我們聲明一個組件,并且給定狀態(tài)count為初始值0 并且告訴React有一個副作用!
  • 給了useEffect函數(shù)里面的匿名函數(shù)就是副作用,我們手動更新DOM就會調(diào)用副作用一次
  • 等到React更新了State狀態(tài)的時候就會執(zhí)行定義的副作用函數(shù)!

useEffecct需要注意的點在哪里

  1. React首次渲染和每次更新都會調(diào)用useEffect函數(shù),而之前如果說使用class的話就會調(diào)用兩個函數(shù)分別為 ComponentDidMount和ComponentDidUpdate
  2. useEffect定義的函數(shù)的執(zhí)行不會阻礙瀏覽器更新視圖! 對應的函數(shù)是異步執(zhí)行的! 而在class組件中的話ComponentDidMount和ComponentDidUpdate都是同步執(zhí)行的!

04|使用useEffect實現(xiàn)ComponentWillUnmount生命周期函數(shù)

對應的其實這一個生命周期函數(shù)還是比較常見的函數(shù),表示組件卸載銷毀之前調(diào)用! 其中對應的應用場景還是比較豐富的! 例如說:

  • 定時器清空,避免發(fā)生內(nèi)存泄露
  • 登錄狀態(tài)的取消操作,避免下次進入信息出錯!

因此在這里就簡單介紹一下使用useEffect實現(xiàn)這個函數(shù)!并且介紹useeffect對應的需要注意的地方!

  • useEffect解綁副作用

對應的在React-Hooks中我們應該改掉所謂的生命周期函數(shù)的概念,取而代之的是 副作用 的概念! 因此對應的ComponentWillUnmount可以理解成為解綁副作用! 在這里為了更好地理解這些東西,我們需要使用react-router

yarn add react-router-dom

對應的路由表配置也在對應的Example04.jsx中完成:

import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Index, List } from "../components/routerComponent";

export default function Example04() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Your Clicked {count} times!</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click me!
      </button>
      <Router>
        <ul>
          <li>
            <Link to="/">首頁</Link>
          </li>
          <li>
            <Link to="/list/">列表</Link>
          </li>
        </ul>
        <Route exact path="/" component={Index} />
        <Route path="/list/" component={List} />
      </Router>
    </div>
  );
}

對應的路由組件的內(nèi)容如下所示:

import React, { useEffect } from "react";
export function Index() {
  useEffect(()=>{
    console.log(`useEffect=>老弟你來了Index界面!`)
  });
  return <h1>Github.com/probedream</h1>;
}
export function List() {
  useEffect(()=>{
    console.log(`useEffect=>老弟你來了List界面!`)
  });
  return <h1>List-page</h1>;
}

對應的當我們沒當點擊一個link的時候都會通過useEffect副作用打印出一個內(nèi)容,對應的打印出對應的內(nèi)容! 此時我們可以通過返回一個函數(shù)的形式進行解綁操作! 此時的代碼如下所示:

import React, { useEffect } from "react";
export function Index() {
  useEffect(() => {
    console.log(`useEffect=>老弟你來了Index界面!`);
    return () => {
      console.log(`老弟你走了離開了Index頁面!`);
    };
  });
  return <h1>Github.com/probedream</h1>;
}
export function List() {
  useEffect(() => {
    console.log(`useEffect=>老弟你來了List界面!`);
    return () => {
      console.log(`老弟你走了離開了List頁面!`);
    };
  });
  return <h1>List-page</h1>;
}

但是修改了對應的代碼之后會發(fā)現(xiàn)沒次點擊都會出現(xiàn) 老弟你走了離開了Index頁面! useEffect=>老弟你來了Index界面!的情況!

對應的每次狀態(tài)發(fā)生變化的時候,useEffect都進行了解綁操作!

  • useEffect的第二個參數(shù)

為了達到Class組件中生命周期 ComponentWillUnmount函數(shù)的效果,只有對應組件被銷毀和卸載之前進行操作,由此我們引申出useEffect的第二個參數(shù):

  • 是一個數(shù)組,數(shù)組中可以寫入很多狀態(tài)對應的變量! 只有對應狀態(tài)發(fā)生變化的時候我們才進行解綁操作!
  • 傳入一個空數(shù)組的時候,空數(shù)組被銷毀的時候才會進行解綁操作! 由此才像ComponentWillUnmount生命周期函數(shù)一樣!

由此代碼進行了下一步的演變:

import React, { useEffect } from "react";
export function Index() {
  useEffect(() => {
    console.log(`useEffect=>老弟你來了Index界面!`);
    return () => {
      console.log(`老弟你走了離開了Index頁面!`);
    };
  }, []);
  return <h1>Github.com/probedream</h1>;
}
export function List() {
  useEffect(() => {
    console.log(`useEffect=>老弟你來了List界面!`);
    return () => {
      console.log(`老弟你走了離開了List頁面!`);
    };
  }, []);
  return <h1>List-page</h1>;
}

其中我們根據(jù)對應的一些代碼,狀態(tài)的變化會導致副作用的解綁!

如果說計時器中加入對應的代碼呢?

import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Index, List } from "../components/routerComponent";

export default function Example04() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(`Your Clicked ${count} times!`);
    return () => {
      console.log("=============");
    };
  }, []);
  return (
    <div>
      <p>Your Clicked {count} times!</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click me!
      </button>
      <Router>
        <ul>
          <li>
            <Link to="/">首頁</Link>
          </li>
          <li>
            <Link to="/list/">列表</Link>
          </li>
        </ul>
        <Route exact path="/" component={Index} />
        <Route path="/list/" component={List} />
      </Router>
    </div>
  );
}

如果說每次都解綁的話,需要 不傳入第二個參數(shù)或者如下所示:

import React,{useEffect,useState} from "react";
export default function Example04(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log(`Your clicked ${count} times`);
        return ()=>{console.log("=============");}
    },[count]);
    return (
        <div>
            <p>Your Clicked {count} times</p>
            <button onClick={()=>setCount(count+1)}>Click me</button>
        </div>
    );
}

05|使用useContext讓父子組件傳值更加簡單

雖然說使用useState和useEffect就已經(jīng)能夠完成大部分的業(yè)務了,但是React-Hooks中還是有很多好用的API,比如說:useContext和useReducer

其中因為現(xiàn)在組件的方式使用的是函數(shù)的形式,不像Class的類組件一樣! 那么對應的父子組件之間如何進行傳值操作呢? 這里就需要介紹到我們的useContext了!

  • useContext解決的是跨組件通信的問題
  • Redux解決的是狀態(tài)統(tǒng)一集中管理的問題

之后會介紹到了useReducer幫助我們實現(xiàn)類似于Redux的功能!

其中的代碼其實比較好理解,代碼如下所示:

import React from "react";
import ReactDOM from "react-dom";
import {Example05} from "../components/Example05";
ReactDOM.render(<Example05/>,document.querySelector("#root"));
import React,{useState,createContext,useContext} from "react";
const CountContext = createContext();
export function Example05(){
    const [count,setCount] = useState(0);
    return (
        <div>
            <p>Your Clicked {count} times!</p>
            <button onClick={()=>{setCount(count+1)}}>Click me!</button>
            <CountContext.Provider value={count}>
                <Counter/>
            </CountContext.Provider>
        </div>
    );
}
export function Counter(){
    const count = useContext(CountContext);
    return <h3>{count}</h3>
}

從對應的代碼中應該不難看出對應的代碼的作用!

  • createContext:創(chuàng)建對應的上下文對象 通過對應的Provider提供者 傳遞對應的狀態(tài)給子組件! 傳入的參數(shù)為default 默認值
  • useContext:使用上下文 **傳入對應的上下文對象可以拿到提供者對象傳遞的變量

06|useReducer介紹和簡單使用

在開發(fā)者使用useReducer可以讓代碼有很好的可讀性和可維護性! 為對應的測試提供方便!

reducer之所以在前端廣泛使用,是因為Redux的緣故,但是并不是只存在于Redux中! 對應的reducer只是一個函數(shù),傳入兩個參數(shù):

  • 一個參數(shù)是狀態(tài)
  • 一個參數(shù)是控制業(yè)務邏輯的判斷參數(shù)

也許這么說可能不那么清楚,但是如果說通過對應的代碼來看的話,可能會好很多

function countReducer(state,action){
    switch(action.type){
        case "add":
            return state + 1;
        case "minus":
            return state - 1;
        default:
            return state;
    }
}

通過以上代碼變很好地說明了兩個參數(shù)之間的差異性!

01|useReducer的使用
import React,{useReducer} from "react";
export default function Example06(){
    const [count,dispatch] = useReducer((state,action)=>{
        switch(action){
            case "add":return state + 1;
            case "minus":return state - 1;
            default : return state;    
        }
    },0);
    return (
        <div>
            <p>現(xiàn)在的分數(shù)為:{count}</p>
            <button onClick={()=>dispatch("add")}>Increment</button>
            <button onClick={()=>dispatch("minus")}>Decrement</button>
        </div>
    );
}

其中需要注意的兩個點:

就是reducer和useReducer的區(qū)別還是有的,例如說switch中的其實是action,在reducer種的是action.type,對應的state中的初始值是在 useReducer中的第二個參數(shù)定義的!

08|useReducer代替Redux案例

對應程序的入口文件main.js:

import React from "react";
import ReactDOM from "react-dom";
import Example from "../Example07/Example07";

ReactDOM.render(<Example07/>,document.querySelector("#root"));

現(xiàn)將程序掛載到首頁容器節(jié)點上!

import React from "react";
import { Color } from "./Color";
import ShowArea from "./ShowArea";
import Button from "./Button";

export default function Example07() {
  return (
    <div>
      <Color>
        <ShowArea />
        <Button />
      </Color>
    </div>
  );
}

其中涉及到三個組件:

  • showArea:顯示字體
  • Button:按鈕組件
  • Color:顏色控制組件
import React, { createContext, useReducer } from "react";

export const ColorContext = createContext({});

export const UPDATE_COLOR = "UPDATE_COLOR";

const reducer = (state, action) => {
  switch (action.type) {
    case UPDATE_COLOR:
      return action.color;
    default:
      return state;
  }
};

export const Color = props => {
  const [color, dispatch] = useReducer(reducer, "blue");
  return (
    <div>
      <ColorContext.Provider value={{ color, dispatch }}>
        {props.children}
      </ColorContext.Provider>
    </div>
  );
};

這里主要是對顏色進行統(tǒng)一設置,由此就涉及到了 前文中講到的內(nèi)容:createContext和useReducer

  • createContext:用來創(chuàng)建上下文對象
  • useReducer:用來創(chuàng)建操作器

這個組件當中向外暴露了三個常量,分別為:

  • ColorContext:顏色上下文對象 傳入為一個空對象 子組件通過useContext拿到的也是空對象!
  • reducer:作為一個函數(shù)根據(jù)對應的參數(shù)類型判斷操作數(shù)據(jù)
  • Color:作為跨層傳遞內(nèi)容的組件 將對應的內(nèi)容傳遞給子組件
import React, { useContext } from "react";
import { UPDATE_COLOR, ColorContext } from "./Color";

export default function Buttons() {
  const { dispatch } = useContext(ColorContext);
  return (
    <div>
      <button onClick={() => dispatch({ type: UPDATE_COLOR, color: "red" })}>
        紅色
      </button>
      <button onClick={() => dispatch({ type: UPDATE_COLOR, color: "yellow" })}>
        黃色
      </button>
    </div>
  );
}
  • 引入對應的UPDATE_COLOR常量和顏色上下文!
import React, { useContext } from "react";
import { ColorContext } from "./Color";
export default function ShowArea() {
  const { color } = useContext(ColorContext);
  return <div style={{ color: color }}>字體顏色為blue</div>;
}

其中我通過以下代碼打印處獲取到的Context對象如下所示:

import React,{useContext} from "react";
import {ColorContext} from "./Color";
console.log(useContext(ColorContext)); // {color,dispatch}

由此不難推導出通過useContext拿到的對象便是value {color,dispatch}對象!

import React,{useReducer,createContext} from "react";

export const UPDATE_COLOR = "UPDATE_COLOR";

export const ColorContext = createContext({});

const Reducer = (state,action)=>{
    switch(action.type){
        case UPDATE_COLOR:return action.color;
        default: return state;    
    }
}
export const Color = props=>{
    const [color,dispatch] = useReducer(Reducer,"blue");
    return (
    <div>
        <ColorContext.Provider value={{color,dispatch}}>
            <props.children/>
        </ColorContext.Provider>
    </div>
    );
}

09|使用useMemo優(yōu)化React-Hooks的程序

import React, { useState } from "react";
import ChildComponent from "./ChildComponent";

export default function Example08() {
  const [xiaoming, setXM] = useState("小明準備中");
  const [xiaowang, setXW] = useState("小王準備中");
  return (
    <div>
      <button
        onClick={() => {
          setXM(new Date().getTime());
        }}
      >
        小明
      </button>
      <button
        onClick={() => {
          setXW(new Date().getDate() + ",小王向我們走來");
        }}
      >
        小王
      </button>
      <ChildComponent name={xiaoming}>{xiaowang}</ChildComponent>
    </div>
  );
}

childComponent

import React from "react";

export default function ChildComponent({ name, children }) {
  function changeXM(name) {
    console.log("小明正在大步向前");
    return name + "小明加油啊!";
  }
  const actionXM = changeXM(name);
  return (
    <div>
      <div>{actionXM}</div>
      <div>{children}</div>
    </div>
  );
}

此時打開對應的程序,你會發(fā)現(xiàn)點擊小王,總是會出現(xiàn)小明正在大步向前每次都運行何嘗不是性能的損耗呢? 因此我們決定使用 新的API useMemo予以解決!

import React,{useMemo} from "reat";
  useMemo(()=>changeXM(name),[name]);

更多的特性講解updating......

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掺冠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌德崭,老刑警劉巖斥黑,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異眉厨,居然都是意外死亡锌奴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門憾股,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹿蜀,“玉大人,你說我怎么就攤上這事服球≤钋。” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵斩熊,是天一觀的道長往枣。 經(jīng)常有香客問我,道長粉渠,這世上最難降的妖魔是什么分冈? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮霸株,結(jié)果婚禮上雕沉,老公的妹妹穿的比我還像新娘。我一直安慰自己去件,他們只是感情好坡椒,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尤溜,像睡著了一般倔叼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上靴跛,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天缀雳,我揣著相機與錄音,去河邊找鬼梢睛。 笑死肥印,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的绝葡。 我是一名探鬼主播深碱,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼藏畅!你這毒婦竟也來了敷硅?” 一聲冷哼從身側(cè)響起功咒,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绞蹦,沒想到半個月后力奋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡幽七,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年景殷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澡屡。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡猿挚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驶鹉,到底是詐尸還是另有隱情绩蜻,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布室埋,位于F島的核電站办绝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏词顾。R本人自食惡果不足惜八秃,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一碱妆、第九天 我趴在偏房一處隱蔽的房頂上張望肉盹。 院中可真熱鬧,春花似錦疹尾、人聲如沸上忍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窍蓝。三九已至,卻和暖如春繁成,著一層夾襖步出監(jiān)牢的瞬間吓笙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工巾腕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留面睛,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓尊搬,卻偏偏與公主長得像叁鉴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子佛寿,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容