對應的代碼倉庫:https://codesandbox.io/s/learning-react-hooks-7ssri
其實對應的React-Hooks帶來的東西很多,其中有什么我們簡單講解一下:
- 使用函數的形式代替原來的繼承類的形式!
- 使用預函數的形式管理對應的state!
- 可以使用React-Hooks來創建帶狀態的組件而并非使用類!
那么對應的我們使用簡單的代碼實例學習React-Hooks! 本文中使用的是CodeSandbox而并非使用 create-react-app創建對應的React項目!
對應的 src/index.js
代碼如下所示:
import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<App/>,document.querySelector("#root"));
01|傳統的類形式的組件與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其實就是所謂的狀態使用,對應的count表示對應的狀態setCount很明顯就是可以改變狀態的方法而使用useState(0)表示當前的count值為0
那么如何理解呢? 在對應的代碼中的表示?
- useState()接收對應的初始值返回一個數組
- 數組的第一位也就是所謂的count表示狀態變量count
- 數組的第二位表示設置狀態變量的方法setCount
02|React-Hooks的多狀態聲明
對應的如何使用多狀態聲明? 上代碼
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是根據useState出現的順序確定的!
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;
如果說你寫下這一行代碼的話,會出現對應的錯誤就是,useState不能夠在ifelse這種條件判斷語句中使用! 不然的話會報錯的!
由此我們可以知道:React Hooks不能出現在條件判斷語句中,因為它必須有完全一樣的渲染順序!
03|useEffect代替常用生命周期函數
其實在我們寫React的時候使用class制作組件的時候,會經常使用到生命周期函數來處理特定階段的事情!
例如說:
- Ajax請求后端的數據
- 添加登錄監聽和取消登錄
- 手動修改DOM等等
那么對應的問題來了? 如果說使用React-Hooks的話有沒有對應的生命周期函數或者說類似于這種功能的方法?
有的,React-Hooks中提供了一種方法叫做useEffect來幫我們解決日常開發中的問題!
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;
通過對應的代碼示例便可以知道對應的效果! 我們梳理一下過程
- 我們聲明一個組件,并且給定狀態count為初始值0 并且告訴React有一個副作用!
- 給了useEffect函數里面的匿名函數就是副作用,我們手動更新DOM就會調用副作用一次
- 等到React更新了State狀態的時候就會執行定義的副作用函數!
useEffecct需要注意的點在哪里
- React首次渲染和每次更新都會調用useEffect函數,而之前如果說使用class的話就會調用兩個函數分別為 ComponentDidMount和ComponentDidUpdate
- useEffect定義的函數的執行不會阻礙瀏覽器更新視圖! 對應的函數是異步執行的! 而在class組件中的話ComponentDidMount和ComponentDidUpdate都是同步執行的!
04|使用useEffect實現ComponentWillUnmount生命周期函數
對應的其實這一個生命周期函數還是比較常見的函數,表示組件卸載銷毀之前調用! 其中對應的應用場景還是比較豐富的! 例如說:
- 定時器清空,避免發生內存泄露
- 登錄狀態的取消操作,避免下次進入信息出錯!
因此在這里就簡單介紹一下使用useEffect實現這個函數!并且介紹useeffect對應的需要注意的地方!
- useEffect解綁副作用
對應的在React-Hooks中我們應該改掉所謂的生命周期函數的概念,取而代之的是 副作用 的概念! 因此對應的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>
);
}
對應的路由組件的內容如下所示:
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副作用打印出一個內容,對應的打印出對應的內容! 此時我們可以通過返回一個函數的形式進行解綁操作! 此時的代碼如下所示:
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>;
}
但是修改了對應的代碼之后會發現沒次點擊都會出現 老弟你走了離開了Index頁面! useEffect=>老弟你來了Index界面!
的情況!
對應的每次狀態發生變化的時候,useEffect都進行了解綁操作!
- useEffect的第二個參數
為了達到Class組件中生命周期 ComponentWillUnmount函數的效果,只有對應組件被銷毀和卸載之前進行操作,由此我們引申出useEffect的第二個參數:
- 是一個數組,數組中可以寫入很多狀態對應的變量! 只有對應狀態發生變化的時候我們才進行解綁操作!
- 傳入一個空數組的時候,空數組被銷毀的時候才會進行解綁操作! 由此才像ComponentWillUnmount生命周期函數一樣!
由此代碼進行了下一步的演變:
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>;
}
其中我們根據對應的一些代碼,狀態的變化會導致副作用的解綁!
如果說計時器中加入對應的代碼呢?
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>
);
}
如果說每次都解綁的話,需要 不傳入第二個參數或者如下所示:
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就已經能夠完成大部分的業務了,但是React-Hooks中還是有很多好用的API,比如說:useContext和useReducer
其中因為現在組件的方式使用的是函數的形式,不像Class的類組件一樣! 那么對應的父子組件之間如何進行傳值操作呢? 這里就需要介紹到我們的useContext了!
- useContext解決的是跨組件通信的問題
- Redux解決的是狀態統一集中管理的問題
之后會介紹到了useReducer幫助我們實現類似于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:創建對應的上下文對象 通過對應的Provider提供者 傳遞對應的狀態給子組件! 傳入的參數為default 默認值
- useContext:使用上下文 **傳入對應的上下文對象可以拿到提供者對象傳遞的變量
06|useReducer介紹和簡單使用
在開發者使用useReducer可以讓代碼有很好的可讀性和可維護性! 為對應的測試提供方便!
reducer之所以在前端廣泛使用,是因為Redux的緣故,但是并不是只存在于Redux中! 對應的reducer只是一個函數,傳入兩個參數:
- 一個參數是狀態
- 一個參數是控制業務邏輯的判斷參數
也許這么說可能不那么清楚,但是如果說通過對應的代碼來看的話,可能會好很多
function countReducer(state,action){
switch(action.type){
case "add":
return state + 1;
case "minus":
return state - 1;
default:
return state;
}
}
通過以上代碼變很好地說明了兩個參數之間的差異性!
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>現在的分數為:{count}</p>
<button onClick={()=>dispatch("add")}>Increment</button>
<button onClick={()=>dispatch("minus")}>Decrement</button>
</div>
);
}
其中需要注意的兩個點:
就是reducer和useReducer的區別還是有的,例如說switch中的其實是action,在reducer種的是action.type,對應的state中的初始值是在 useReducer中的第二個參數定義的!
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"));
現將程序掛載到首頁容器節點上!
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>
);
};
這里主要是對顏色進行統一設置,由此就涉及到了 前文中講到的內容:createContext和useReducer
- createContext:用來創建上下文對象
- useReducer:用來創建操作器
這個組件當中向外暴露了三個常量,分別為:
- ColorContext:顏色上下文對象 傳入為一個空對象 子組件通過useContext拿到的也是空對象!
- reducer:作為一個函數根據對應的參數類型判斷操作數據
- Color:作為跨層傳遞內容的組件 將對應的內容傳遞給子組件
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優化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>
);
}
此時打開對應的程序,你會發現點擊小王,總是會出現小明正在大步向前每次都運行何嘗不是性能的損耗呢? 因此我們決定使用 新的API useMemo予以解決!
import React,{useMemo} from "reat";
useMemo(()=>changeXM(name),[name]);
更多的特性講解updating......