最近要對舊的項目進行重構,統一使用全新的react技術棧。同時,咱們也決定嘗試使用React hooks來進行開發,可是,因爲React hooks崇尚的是使用(也只能使用)function component的形式來進行開發,而不是class component,所以,整個開發方式也會與以前產生比較大的差別。因此,我這裏就積累了下實際項目中遇到的問題以及思考,看下能不能幫助你們少走彎路。javascript
接下來就直接進入正文。我會將項目中遇到的問題一一列舉出來,而且給出解決方案。html
當我轉到React hooks的時候,首先就遇到了這個問題:java
通常來講,業務組件常常會遇到要經過發起ajax請求來獲取業務數據而且執行初始化操做的場景。在使用class component編程的時候,咱們就能夠在class component提供的生命週期鉤子函數(好比componentDidMount, constructor等)執行這個操做。但是若是轉到React hooks以後,function component裏是沒有這個生命週期鉤子函數的,那這個初始化操做怎麼辦呢?總不能每次遇到這種場景都使用class component來作吧?react
解決方案:使用useEffect(想知道useEffect是什麼的話,能夠點擊這裏)git
useEffect,顧名思義,就是執行有反作用的操做,你能夠把它當成componentDidMount
, componentDidUpdate
, and componentWillUnmount
的集合。它的函數聲明以下github
useEffect(effect: React.EffectCallback, inputs?: ReadonlyArray<any> | undefined)
那麼,咱們在實際使用中,咱們就可使用這個來執行初始化操做。舉個例子ajax
import React, { useEffect } from 'react' export function BusinessComponent() { const initData = async () => { // 發起請求並執行初始化操做 } // 執行初始化操做,須要注意的是,若是你只是想在渲染的時候初始化一次數據,那麼第二個參數必須傳空數組。 useEffect(() => { initData(); }, []); return (<div></div>); }
須要注意的是,這裏的useEffect的第二個參數必須傳空數組,這樣它就等價於只在componentDidMount的時候執行。若是不傳第二個參數的話,它就等價於componentDidMount和componentDidUpdate編程
因爲咱們在實際開發過程當中,常常會遇到須要作一些反作用的場景,好比輪詢操做(定時器、輪詢請求等)、使用瀏覽器原生的事件監聽機制而不用react的事件機制(這種狀況下,組件銷燬的時候,須要用戶主動去取消事件監聽)等。使用class Component編程的時候,咱們通常都在componentWillUnmount或者componentDidUnmount的時候去作清理操做,但是使用react hooks的時候,咱們如何作處理呢?數組
解決方案:使用useEffect第一個參數的返回值瀏覽器
若是useEffect的第一個參數返回了函數的時候,react會在每一次執行新的effects以前,執行這個函數來作一些清理操做。所以,咱們就可使用它來執行一些清理操做。
例子:好比咱們要作一個二維碼組件,咱們須要根據傳入的userId不斷輪詢地向後臺發請求查詢掃描二維碼的狀態,這種狀況下,咱們就須要在組件unmount的時候清理掉輪詢操做。代碼以下:
import React, { useEffect } from 'react' export function QRCode(url, userId) { // 根據userId查詢掃描狀態 const pollingQueryingStatus = async () => { } // 取消輪詢 const stopPollingQueryStatus = async() => { } useEffect(() => { pollingQueryingStatus(); return stopPollingQueryStatus; }, []); // 根據url生成二維碼 return (<div></div>) }
這樣的話,就等價於在componentWillUnmount的時候去執行清理操做。
可是,有時候咱們可能須要執行屢次清理操做。仍是舉上面的例子,咱們須要在用戶傳入新的userId的時候,去執行新的查詢的操做,同時咱們還須要清除掉舊的輪詢操做。想一下怎麼作比較好。
其實對這種狀況,官方也已經給出瞭解決方案了,useEffect的第二個參數是觸發effects的關鍵,若是用戶傳入了第二個參數,那麼只有在第二個參數的值發生變化(以及首次渲染)的時候,纔會觸發effects。所以,咱們只須要將上面的代碼改一下:
import React, { useEffect } from 'react' export function QRCode(url, userId) { // 根據userId查詢掃描狀態 const pollingQueryingStatus = async () => { } const stopPollingQueryStatus = async() => { } // 咱們只是將useEffect的第二個參數加了個userId useEffect(() => { pollingQueryingStatus(); return stopPollingQueryStatus; }, [userId]); // 根據url生成二維碼 return (<div></div>) }
咱們只是在useEffect的第二個參數數組裏,加入了一個userId。這樣的話,userId的每一次變化都會先觸發stopPollingQueryStatus,以後再執行effects,這樣就能夠達到咱們的目的。
react hooks使用useState來代替class Component裏的state。但是,在具體開發過程當中,我也發現了一些不一樣點。useState介紹能夠點擊這裏
在setState的時候,咱們能夠只修改state中的局部變量,而不須要將整個修改後的state傳進去,舉個例子
import React, { PureComponent } from 'react'; export class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0, name: 'cjg', age: 18, } } handleClick = () => { const { count } = this.state; // 咱們只須要傳入修改的局部變量 this.setState({ count: count + 1, }); } render() { return ( <button onClick={this.handleClick}></button> ) } }
而使用useState後,咱們修改state必須將整個修改後的state傳入去,由於它會直接覆蓋以前的state,而不是合併以前state對象。
import React, { useState } from 'react'; export function Count() { const [data, setData] = useState({ count: 0, name: 'cjg', age: 18, }); const handleClick = () => { const { count } = data; // 這裏必須將完整的state對象傳進去 setData({ ...data, count: count + 1, }) }; return (<button onClick={handleClick}></button>) }
在使用class Component進行開發的時候,咱們可使用shouldComponentUpdate
來減小沒必要要的渲染,那麼在使用react hooks後,咱們如何實現這樣的功能呢?
解決方案:React.memo和useMemo
對於這種狀況,react固然也給出了官方的解決方案,就是使用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, name: 'cjg', age: 18, }); const handleClick = () => { const { count } = data; setData({ ...data, count: count + 1, }) }; return (<button onClick={handleClick}>count:{data.count}</button>) });
useMemo它的用法其實跟useEffects有點像,咱們直接看官方給的例子
function Parent({ a, b }) { // Only re-rendered if `a` changes: const child1 = useMemo(() => <Child1 a={a} />, [a]); // Only re-rendered if `b` changes: const child2 = useMemo(() => <Child2 b={b} />, [b]); return ( <> {child1} {child2} </> ) }
從例子能夠看出來,它的第二個參數和useEffect的第二個參數是同樣的,只有在第二個參數數組的值發生變化時,纔會觸發子組件的更新。
一開始在從class component轉變到react hooks的時候,確實很不適應。但是當我習慣了這種寫法後,個人心情以下:
固然,如今react hooks仍是在alpha階段,若是你們以爲不放心的話,能夠再等等。反正我就先下手玩玩了哈哈哈。
本文地址在->本人博客地址, 歡迎給個 start 或 follow