摘要:可以在不改變組件層級(jí)的前提下將帶有狀態(tài)的邏輯抽離出來(lái)。因此在中增加了一個(gè)特性,允許傳入的函數(shù)再返回一個(gè)函數(shù),這個(gè)返回函數(shù)的執(zhí)行時(shí)機(jī)是下一次觸發(fā)這個(gè)前,以及組件卸載前。當(dāng)?shù)诙€(gè)參數(shù)為空數(shù)組時(shí),返回函數(shù)進(jìn)行清理工作只會(huì)在組件卸載時(shí)執(zhí)行。
hooks是什么
hooks是react16.8版本中新增的特性,它讓我們能夠在不寫(xiě)class的情況下使用狀態(tài)和其他react特性。也就是說(shuō)現(xiàn)在我們可以在函數(shù)組件中進(jìn)行狀態(tài)管理了。
hooks解決了什么問(wèn)題組件中帶狀態(tài)的邏輯很難復(fù)用
在hooks之前解決這個(gè)問(wèn)題的手段是render props和高階組件。但是這些方法都需要我們?nèi)バ薷慕M件層級(jí)關(guān)系,讓代碼變得很繁瑣。
hooks可以在不改變組件層級(jí)的前提下將帶有狀態(tài)的邏輯抽離出來(lái)。
復(fù)雜組件讓人難以理解
原先用class寫(xiě)的組件集成了許多生命周期函數(shù),這些生命周期函數(shù)中又包含了許多互不相關(guān)的邏輯,比如接口請(qǐng)求,事件綁定等等。這導(dǎo)致組件邏輯復(fù)雜之后難以理解也難以測(cè)試。
hooks可以將復(fù)雜組件的邏輯拆分成更小的函數(shù),這些函數(shù)只負(fù)責(zé)單一的邏輯。拆分后的優(yōu)點(diǎn)是易懂易測(cè)試。
class本身存在的問(wèn)題
盡管class已經(jīng)是一個(gè)語(yǔ)法糖了,但是react的開(kāi)發(fā)者認(rèn)為this是一個(gè)很麻煩的東西,我們?cè)谟胏lass寫(xiě)組件前必須先搞清楚this的工作原理。另外class組件在react編譯過(guò)程中也存在一些問(wèn)題,如壓縮率并不是很好,熱加載不穩(wěn)定等等。
hooks可以讓我們?cè)诤瘮?shù)組件中管理狀態(tài)。盡管完全拋棄class組件還為時(shí)尚早,但是有了hooks我們使用class組件的機(jī)會(huì)將越來(lái)越少。
基本用法:
import React, { useState } from "react"; export default function Foo() { const [selectedKey, setSelectedKey] = useState(""); const someHandler = () => { setSelectedKey("1") }; ... return ...; }
上面例子中,useState函數(shù)會(huì)返回一個(gè)數(shù)組,數(shù)組第一項(xiàng)是我們定義的一個(gè)狀態(tài)selectedKey,第二項(xiàng)是修改這個(gè)狀態(tài)的函數(shù),而userState接收的參數(shù)就是這個(gè)狀態(tài)的初始值。當(dāng)我們使用setSelectedKey修改狀態(tài)時(shí),react會(huì)重新渲染該組件,效果跟setState一樣。
這段代碼改成class的寫(xiě)法是這樣的:
import React, { Component } from "react"; export default class Foo extends Component { state = { selectedKey: "" } someHandler = () => { this.setState({selectedKey: "1"}); } ... render() { return ...; } }
區(qū)別:
使用class時(shí),我們把組件的所有狀態(tài)都放在state這個(gè)對(duì)象中;而一個(gè)useState只定義一個(gè)狀態(tài)量,組件有幾個(gè)狀態(tài)變量就寫(xiě)幾行useState
setState是將新?tīng)顟B(tài)merge到老狀態(tài)中,而useState返回的函數(shù)setSelectedKey是將新?tīng)顟B(tài)替換老狀態(tài),因?yàn)橐粋€(gè)useState只定義一個(gè)狀態(tài)量所以這邊直接替換是沒(méi)有問(wèn)題的
useEffect顧名思義,useEffect就是去做一些有副作用的事。默認(rèn)情況下useEffect接收一個(gè)函數(shù)作為參數(shù),在每次render結(jié)束后react會(huì)去執(zhí)行這個(gè)函數(shù),效果相當(dāng)于componentDidMount和componentDidUpdate的組合。
下面這段代碼表示每次渲染后將狀態(tài)selectedKey的值設(shè)為home:
import React, { useState, useEffect } from "react"; export default function Foo() { const [selectedKey, setSelectedKey] = useState(""); useEffect(()=> { setSelectedKey("home"); }); ... return ...; }
useEffect的功能還不止這么簡(jiǎn)單,當(dāng)我們進(jìn)行某些副作用操作后,往往需要在組件卸載前做一些清理工作,比如清除定時(shí)器,解綁事件監(jiān)聽(tīng)器等等。因此react在useEffect中增加了一個(gè)特性,允許傳入的函數(shù)再返回一個(gè)函數(shù),這個(gè)返回函數(shù)的執(zhí)行時(shí)機(jī)是下一次觸發(fā)這個(gè)useEffect前,以及組件卸載前。
如果我們只想在組件卸載前進(jìn)行一些清理工作,那就要用到uesEffect的第二個(gè)參數(shù)了。第二個(gè)參數(shù)是一個(gè)數(shù)組,里面可以放這個(gè)副作用的依賴(lài),作用是控制這個(gè)副作用執(zhí)行的時(shí)機(jī),只有當(dāng)依賴(lài)發(fā)生變化的時(shí)候才會(huì)執(zhí)行這個(gè)副作用。當(dāng)?shù)诙€(gè)參數(shù)為空數(shù)組時(shí),返回函數(shù)(進(jìn)行清理工作)只會(huì)在組件卸載時(shí)執(zhí)行。
下面這個(gè)例子的作用是在組件首次渲染后以及props.source的值發(fā)生變化后執(zhí)行subscribe( ),在下一次執(zhí)行subscribe( )前以及組件卸載前執(zhí)行unsubscribe( ):
useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source] );
使用useEffect可以模擬react的某些生命周期函數(shù)
useEffect(() => { // 這里在mount時(shí)執(zhí)行一次 }, []);
useEffect(() => { // 這里在mount時(shí)執(zhí)行一次 return () => { // 這里在unmount時(shí)執(zhí)行一次 } }, []);
// useRef會(huì)返回一個(gè)對(duì)象,這個(gè)對(duì)象有個(gè)current屬性,值為傳給useRef的參數(shù) // useRef在組件生命周期中只初始化一次,之后它會(huì)幫我們保存返回的對(duì)象 const mounting = useRef(true); useEffect(() => { if (mounting.current) { mounting.current = false; // 對(duì)current的修改會(huì)被useRef保存,但修改不會(huì)引起重新渲染 } else { // 這里只在update時(shí)執(zhí)行 } });舉例
需求:使用antd的menu組件實(shí)現(xiàn)一個(gè)側(cè)邊欄,類(lèi)似下面的樣子,當(dāng)用戶輸入不同url時(shí)側(cè)邊欄需要聯(lián)動(dòng)
實(shí)現(xiàn):
import React, { useState, useEffect } from "react"; import { Menu } from "antd"; export default function Sider() { const [selectedKey, setSelectedKey] = useState(""); const [openKeys, setOpenKeys] = useState([]); // 組件更新時(shí)根據(jù)url更新選中的菜單項(xiàng) useEffect(() => { const key = getSelectedKey(); // 根據(jù)url得到選中的菜單項(xiàng) setSelectedKey(key); }); // 組件mount時(shí)根據(jù)url自動(dòng)展開(kāi)子菜單 useEffect(() => { const key = getOpenKey(); // 根據(jù)url得到應(yīng)該展開(kāi)的菜單 setOpenKeys([key]); }, []); ... return ( ); }
分析:
組件使用useState定義了兩個(gè)狀態(tài)量:selectedKey和openKeys;
第一個(gè)useEffect用于更新selectedKey,它會(huì)在每次render后從url中獲取當(dāng)前選中的菜單項(xiàng),然后更新selectedKey;
第二個(gè)useEffect用于首次進(jìn)入網(wǎng)站時(shí),從url中獲取應(yīng)該展開(kāi)的菜單并更新openKeys,它只在組件創(chuàng)建時(shí)執(zhí)行,相當(dāng)于componentDidMount;
當(dāng)用戶點(diǎn)擊父菜單想要展開(kāi)或收起時(shí),通過(guò)onOpenChange事件來(lái)觸發(fā)openKeys的更新;
當(dāng)用戶點(diǎn)擊子菜單想要選中時(shí),會(huì)先觸發(fā)路由跳轉(zhuǎn)(這個(gè)邏輯無(wú)法從代碼中獲取,請(qǐng)自行腦補(bǔ)),路由改變會(huì)引發(fā)改組件重新渲染,繼而觸發(fā)第一個(gè)useEffect來(lái)更新selectedKey。
自定義hooks文章開(kāi)頭已經(jīng)講到了hooks可以很方便的實(shí)現(xiàn)帶狀態(tài)的邏輯復(fù)用。
下面是一個(gè)簡(jiǎn)單的自定義hooks,功能是請(qǐng)求接口并返回?cái)?shù)據(jù):
import { useState, useEffect } from "react"; export default function useUserInfo() { const [userInfo, setUserInfo] = useState(null); useEffect(() => { fetch("https://react.hooks.com/api/userinfo").then( data => { setUserInfo(data); }, ); }, []); return userInfo; }
使用也很簡(jiǎn)單,當(dāng)Home組件加載時(shí)會(huì)通過(guò)useUserInfo這個(gè)自定義hooks去請(qǐng)求接口,接口數(shù)據(jù)返回時(shí)Home組件會(huì)自動(dòng)更新:
import React from "react"; import useUserInfo from "../../hooks/useUserInfo"; export default function Home() { const userInfo = useUserInfo(); returnhooks + context進(jìn)行全局狀態(tài)管理{userInfo}; }
react提供了useContext這個(gè)hooks使得在函數(shù)組件中使用context變得更加方便。
如果項(xiàng)目沒(méi)有復(fù)雜到需要上redux,可以使用下面的方法進(jìn)行全局狀態(tài)管理。
首先創(chuàng)建一個(gè)context:
// globalContext.js import React from "react"; export default React.createContext({ musicianPlan: "1", language: "zh", changeMusicianPlan: () => {}, changeLanguage: () => {}, });
然后定義一個(gè)高階組件,用于管理context中的狀態(tài):
// globalState.jsx import React, { useState } from "react"; import GlobalContext from "./globalContext"; export default function GlobalState(props) { const [musicianPlan, setMusicianPlan] = useState("1"); const [language, setLanguage] = useState("zh"); const changeMusicianPlan = planId => { setMusicianPlan(planId); }; const changeLanguage = lang => { setLanguage(lang); }; return ({props.children} ); }
將這個(gè)高階組件放到組件樹(shù)的頂層:
// app.jsx import React from "react"; import GlobalState from "./context/globalState"; export default function App() { return (); } ...
在Header組件中用useContext這個(gè)hooks獲取到context,然后調(diào)用changeMusicianPlan方法來(lái)改變?nèi)譅顟B(tài)musicianPlan:
import React, { useContext, ReactElement } from "react"; import { Select } from "antd"; import GlobalContext from "../../context/globalContext"; const Option = Select.Option; export default function Header() { const globalContext = useContext(GlobalContext); return ( ); }
在Home組件中同樣使用useContext獲取context,然后使用全局狀態(tài)musicianPlan進(jìn)行動(dòng)態(tài)渲染:
import React, { useContext } from "react"; import GlobalContext from "../../context/globalContext"; export default function Home() { const globalContext = useContext(GlobalContext); return{globalContext.musicianPlan}; }
當(dāng)然上面的方法也可以用于某個(gè)局部組件樹(shù)的狀態(tài)管理,將狀態(tài)進(jìn)行拆分管理不僅提高運(yùn)行效率也更清晰易懂。
使用hooks的注意事項(xiàng)hooks只能在組件內(nèi)部的最頂層調(diào)用,不能將其放在循環(huán)語(yǔ)句、條件語(yǔ)句或者子函數(shù)內(nèi);
hooks只能在函數(shù)組件或自定義hooks中使用;
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/103726.html
摘要:前言的基本概念組件的構(gòu)建方法以及高級(jí)用法這背后的一切如何運(yùn)轉(zhuǎn)深入內(nèi)部的實(shí)現(xiàn)機(jī)制和原理初探源碼代碼組織結(jié)構(gòu)包含一系列的工具方法插件包含一系列同構(gòu)方法包含一些公用或常用方法如等包含一些測(cè)試方法等包含一些邊界錯(cuò)誤的測(cè)試用例是代碼的核心部分它包含了 前言 React的基本概念,API,組件的構(gòu)建方法以及高級(jí)用法,這背后的一切如何運(yùn)轉(zhuǎn),深入Virtual DOM內(nèi)部的實(shí)現(xiàn)機(jī)制和原理. 初探Rea...
摘要:各個(gè)組件維護(hù)自己的狀態(tài)和,當(dāng)狀態(tài)變更,自動(dòng)重新渲染整個(gè)組件。形式的定義的組件是以的形式來(lái)創(chuàng)建的組件的,是目前極為推薦的創(chuàng)建有狀態(tài)組件的方式,最終會(huì)取代形式相對(duì)于可以更好實(shí)現(xiàn)代碼復(fù)用。組件名稱(chēng)首字母必須大寫(xiě)。變量名用包裹,且不能加雙引號(hào)。 目前在前端開(kāi)發(fā)領(lǐng)域,框架Angular、react和vue占據(jù)著主流的地位而且可能會(huì)持續(xù)比較長(zhǎng)的一段時(shí)間。三門(mén)框架中,從數(shù)據(jù)綁定機(jī)制來(lái)看,vue和an...
摘要:各個(gè)組件維護(hù)自己的狀態(tài)和,當(dāng)狀態(tài)變更,自動(dòng)重新渲染整個(gè)組件。形式的定義的組件是以的形式來(lái)創(chuàng)建的組件的,是目前極為推薦的創(chuàng)建有狀態(tài)組件的方式,最終會(huì)取代形式相對(duì)于可以更好實(shí)現(xiàn)代碼復(fù)用。組件名稱(chēng)首字母必須大寫(xiě)。變量名用包裹,且不能加雙引號(hào)。 目前在前端開(kāi)發(fā)領(lǐng)域,框架Angular、react和vue占據(jù)著主流的地位而且可能會(huì)持續(xù)比較長(zhǎng)的一段時(shí)間。三門(mén)框架中,從數(shù)據(jù)綁定機(jī)制來(lái)看,vue和an...
摘要:它的作用就是像它的名字那樣,建立一個(gè)從外部的對(duì)象到組件的對(duì)象的映射關(guān)系。比如表示從整個(gè)的表示當(dāng)前組件容器的用來(lái)建立組件的參數(shù)到方法的映射比如表示它定義了哪些用戶的操作應(yīng)該當(dāng)作,傳給。 最近做的項(xiàng)目加入了react-redux,對(duì)react-redux一直沒(méi)理解透徹,最近有時(shí)間把react-redux梳理了一番,希望能夠幫助到大家, 首先有這幾個(gè)文件,action,reducer,sag...
摘要:相信用的同學(xué)也不少找到函數(shù)在其中中添加啟用編譯。。。react 最近已經(jīng)開(kāi)始使用react技術(shù)棧了,從頭開(kāi)始搭建項(xiàng)目,有必要的記錄一下配置的過(guò)程以及項(xiàng)目分層的思路,這次后臺(tái)項(xiàng)目采用的主要采用react-create-app腳手架以及Ant DesignUI 以及多語(yǔ)言react-intl create-react-app 這是官方維護(hù)的腳手架應(yīng)用 我們一般也采用這個(gè) $ npm or c...
閱讀 2226·2021-11-11 16:55
閱讀 1745·2019-08-30 15:54
閱讀 2883·2019-08-30 15:53
閱讀 2276·2019-08-30 15:44
閱讀 1212·2019-08-30 15:43
閱讀 1015·2019-08-30 11:22
閱讀 2017·2019-08-29 17:20
閱讀 1619·2019-08-29 16:56