日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

React Hooks的丑陋一面

 

在這篇文章中,我將分享我對(duì)React Hooks的觀點(diǎn),正如這篇文章的標(biāo)題所暗示的那樣,我不是一個(gè)忠實(shí)的粉絲。

讓我們來分析一下React官方的文檔中描述的放棄類而使用鉤子的動(dòng)機(jī)。

動(dòng)機(jī)1:class令人困惑

我們發(fā)現(xiàn),class可能是學(xué)習(xí)React的一大障礙,你必須了解 this 在JAVAScript中的工作方式,這與大多數(shù)語言中的工作方式截然不同。你必須記住要綁定事件處理程序,代碼會(huì)非常啰嗦,React中函數(shù)和類組件之間的區(qū)別,以及何時(shí)使用每個(gè)組件,甚至在有經(jīng)驗(yàn)的React開發(fā)人員之間也會(huì)導(dǎo)致分歧。

好吧,我可以同意 this 在你剛開始使用JavaScript的時(shí)候可能會(huì)有點(diǎn)混亂,但是箭頭函數(shù)解決了混亂,把一個(gè)已經(jīng)被Typescript開箱即用支持的第三階段功能稱為“不穩(wěn)定的語法建議”,這純粹是煽動(dòng)性的。React團(tuán)隊(duì)指的是class字段語法,該語法已經(jīng)被廣泛使用并且可能很快會(huì)得到正式支持:

class Foo extends React.Component {
  onPress = () => {
    console.log(this.props.someProp);
  }

  render() {
    return <Button onPress={this.onPress} />
  }
}

如你所見,通過使用class字段箭頭函數(shù),你無需在構(gòu)造函數(shù)中綁定任何內(nèi)容,并且它始終指向正確的上下文。

如果Class令人困惑,那么對(duì)于新的鉤子函數(shù)我們能說些什么呢?鉤子函數(shù)不是常規(guī)函數(shù),因?yàn)樗哂袪顟B(tài),看起來很奇怪的 this(又名 useRef ),并且可以具有多個(gè)實(shí)例。但這絕對(duì)不是類,介于兩者之間,從現(xiàn)在開始,我將其稱為 Funclass。那么,對(duì)于人類和機(jī)器而言,那些Funclass會(huì)更容易嗎?我不確定機(jī)器,但我真的不認(rèn)為Funclass從概念上比類更容易理解。

類是一個(gè)眾所周知的思想概念,每個(gè)開發(fā)人員都熟悉 this 的概念,即使在javascript中也有所不同。另一方面,F(xiàn)unclass是一個(gè)新概念,一個(gè)很奇怪的概念。它們讓人感覺更神奇,而且它們過于依賴慣例而不是嚴(yán)格的語法。你必須遵循一些嚴(yán)格而奇怪的規(guī)則,你需要小心你的代碼放在哪里,而且有很多陷阱。還要準(zhǔn)備好一些可怕的命名,比如 useRef( this 的花哨名字)、useEffect、useMemo、useImperativeHandle(說什么呢?)等等。

類的語法是為了處理多實(shí)例的概念和實(shí)例范圍的概念(this 的確切目的)而專門發(fā)明的。Funclass只是一種實(shí)現(xiàn)相同目標(biāo)的奇怪方式,許多人將Funclass與函數(shù)式編程相混淆,但Funclass實(shí)際上只是變相的類。類是一個(gè)概念,而不是語法。

在React中,函數(shù)和類組件之間的區(qū)別,以及何時(shí)使用每一種組件,甚至在有經(jīng)驗(yàn)的React開發(fā)人員之間也會(huì)產(chǎn)生分歧。

到目前為止,這種區(qū)別非常明顯——如果需要狀態(tài)或生命周期方法,則使用類,否則,使用函數(shù)或類實(shí)際上并不重要。就我個(gè)人而言,我很喜歡這樣的想法:當(dāng)我偶然發(fā)現(xiàn)一個(gè)函數(shù)組件時(shí),我可以立即知道這是一個(gè)沒有狀態(tài)的“啞巴組件”。遺憾的是,隨著Funclasses的引入,情況不再是這樣了。

動(dòng)機(jī)2:很難在組件之間重用有狀態(tài)邏輯

具有諷刺意味嗎?至少在我看來,React最大的問題是它沒有提供一個(gè)開箱即用的狀態(tài)管理方案,讓我們對(duì)應(yīng)該如何填補(bǔ)這個(gè)空白的問題爭論了很久,也為Redux等一些非常糟糕的設(shè)計(jì)模式打開了一扇門。所以在經(jīng)歷了多年的挫折之后,React團(tuán)隊(duì)終于得出了一個(gè)結(jié)論:組件之間很難共享有狀態(tài)邏輯......誰能想到呢?

無論如何,勾子會(huì)使情況變得更好嗎?答案是不盡然。鉤子不能和類一起工作,所以如果你的代碼庫已經(jīng)用類來編寫,你還是需要另一種方式來共享有狀態(tài)的邏輯。另外,鉤子只解決了每個(gè)實(shí)例邏輯共享的問題,但如果你想在多個(gè)實(shí)例之間共享狀態(tài),你仍然需要使用stores和第三方狀態(tài)管理解決方案,正如我所說,如果你已經(jīng)使用它們,你并不真正需要鉤子。

所以,與其只是治標(biāo)不治本,或許React是時(shí)候行動(dòng)起來,實(shí)現(xiàn)一個(gè)合適的狀態(tài)管理工具,同時(shí)管理全局狀態(tài)(stores)和本地狀態(tài)(每個(gè)實(shí)例),從而徹底扼殺這個(gè)漏洞。

動(dòng)機(jī)3:復(fù)雜的組件變得難以理解

如果你已經(jīng)在使用stores,這種說法幾乎沒有意義,讓我們看看為什么。

class Foo extends React.Component {
  componentDidMount() {
    doA(); 
    doB(); 
    doC();
  }
}

在這個(gè)例子中,你可以看到,我們可能在 componentDidMount 中混合了不相關(guān)的邏輯,但這是否會(huì)使我們的組件膨脹?不完全是。整個(gè)實(shí)現(xiàn)位于類之外,而狀態(tài)位于store中,沒有store 所有狀態(tài)邏輯都必須在類內(nèi)部實(shí)現(xiàn),而該類確實(shí)會(huì)臃腫。但看起來React又解決了一個(gè)問題,這個(gè)問題大多存在于一個(gè)沒有狀態(tài)管理工具的世界里。實(shí)際上,大多數(shù)大型應(yīng)用程序已經(jīng)在使用狀態(tài)管理工具,并且該問題已得到緩解。另外,在大多數(shù)情況下,我們也許可以將這個(gè)類分解成更小的組件,并將每個(gè) doSomething() 放在子組件的 componentDidMount 中。

使用Funclass,我們可以編寫如下代碼:

function Foo() {
  useA(); 
  useB(); 
  useC();
}

看起來有點(diǎn)干凈,但是是嗎?我們還需要在某個(gè)地方寫3個(gè)不同的useEffect鉤子,所以最后我們要寫更多的代碼,看看我們?cè)谶@里做了什么——有了類組件,你可以一目了然地知道組件在mount上做什么。在Funclass的例子中,你需要按照鉤子并嘗試搜索帶有空依賴項(xiàng)數(shù)組的 useEffect,以了解組件在mount上做什么。生命周期方法的聲明性本質(zhì)上是一件好事,我發(fā)現(xiàn)研究Funclasss的流程要困難得多。我見過很多案例是Funclasses讓開發(fā)者更容易寫出糟糕的代碼,我們后面會(huì)看到一個(gè)例子。

但是首先,我必須承認(rèn) useEffect 有一些好處,請(qǐng)看以下示例:

useEffect(() => {
  subscribeToA();
  return () => {
    unsubscribeFromA();
  };
 }, []);

useEffect 鉤子讓我們將訂閱和退訂邏輯配對(duì)在一起。這其實(shí)是一個(gè)非常整潔的模式,同樣的,把 componentDidMount 和 componentDidUpdate 配對(duì)在一起也是如此。以我的經(jīng)驗(yàn),這些情況并不常見,但它們?nèi)匀皇怯行У挠美谶@里 useEffect 確實(shí)很有用。問題是,為什么我們必須使用Funclass才能獲得 useEffect?為什么我們的Class不能有類似的東西?答案是我們可以:

class Foo extends React.Component {
   someEffect = effect((value1, value2) => {
     subscribeToA(value1, value2);
     return () => {
        unsubscribeFromA();
     };
   })
   render(){ 
    this.someEffect(this.props.value1, this.state.value2);
    return <Text>Hello world</Text>   
   }
}

effect 函數(shù)將記住給定的函數(shù),并且僅當(dāng)其參數(shù)之一已更改時(shí)才會(huì)再次調(diào)用它。通過從我們的render函數(shù)內(nèi)部觸發(fā)效果,我們可以確保它在每次渲染/更新時(shí)都被調(diào)用,但只有當(dāng)它的一個(gè)參數(shù)被改變時(shí),給定的函數(shù)才會(huì)再次運(yùn)行,所以我們?cè)诮Y(jié)合 componentDidMount 和 componentDidUpdate 方面實(shí)現(xiàn)了類似 useEffect 的效果,但遺憾的是,我們?nèi)匀恍枰?nbsp;componentWillUnmount 中手動(dòng)進(jìn)行最后的清理。另外,從render內(nèi)調(diào)用效果函數(shù)也有點(diǎn)丑。為了得到和useEffect完全一樣的效果,React需要增加對(duì)它的支持。

最重要的是 useEffect 不應(yīng)該被認(rèn)為是進(jìn)入funclass的有效動(dòng)機(jī),它本身就是一個(gè)有效的動(dòng)機(jī),也可以為類實(shí)現(xiàn)。

動(dòng)機(jī)4:性能

React團(tuán)隊(duì)說類很難優(yōu)化和最小化,funclass應(yīng)該以某種方式改進(jìn),關(guān)于這件事,我只有一件事要說——給我看看數(shù)字

我至今找不到任何論文,也沒有我可以克隆并運(yùn)行以比較Funclasses VS Class的性能的基準(zhǔn)演示應(yīng)用程序。事實(shí)上,我們沒有看到這樣的演示并不奇怪——Funclasses需要以某種方式實(shí)現(xiàn)這個(gè)功能(如果你喜歡的話,也可以用Ref),所以我很期待那些讓類難以優(yōu)化的問題,也會(huì)影響到Funclasses。

不管怎么說,所有關(guān)于性能的爭論,在不展示數(shù)據(jù)的情況下實(shí)在是一文不值,所以我們真的不能把它作為論據(jù)。

動(dòng)機(jī)5:Funclass不太冗長

你可以找到很多通過將Class轉(zhuǎn)換為Funclass來減少代碼的例子,但大多數(shù)甚至所有的例子都利用了 useEffect 鉤子,以便將 componentDidMount 和 componentWillUnmount 結(jié)合在一起,從而達(dá)到極大的效果。

但正如我前面所說,useEffect 不應(yīng)該被認(rèn)為是Funclass的優(yōu)勢(shì),如果忽略它所實(shí)現(xiàn)的代碼減少,那么只會(huì)留下非常小的影響。而且,如果你嘗試使用 useMemo,useCallback 等來優(yōu)化Funclass,你甚至可能得到比等效類更冗長的代碼。

當(dāng)比較小而瑣碎的組件時(shí),F(xiàn)unclasses毫無疑問地贏了,因?yàn)轭愑幸恍┕逃械哪0澹瑹o論你的類有多小你都需要付出。但在比較大的組件時(shí),你幾乎看不出差別,有時(shí)正如我所說,類甚至可以更干凈。

最后,我不得不對(duì) useContext 說幾句:useContext其實(shí)比我們目前原有的類的context API有很大的改進(jìn)。但是再一次,為什么我們不能為類也有這樣漂亮而簡潔的API呢? 為什么我們不能做這樣的事情。

//inside "./someContext" :
export const someContext = React.Context({helloText: 'bla'});

//inside "Foo":
import {someContext} from './someContext';

class Foo extends React.component {
   render() {
      <View>
        <Text>{someContext.helloText}</Text>
      </View>
   }
}

當(dāng)上下文中的 helloText 發(fā)生變化時(shí),組件應(yīng)該重新渲染以反映這些變化。就是這樣,不需要丑陋的高階組件(HOC)。

那么,為什么React團(tuán)隊(duì)選擇只改進(jìn)useContext API而不是常規(guī)content API?我不知道,但這并不意味著Funclass本質(zhì)上更干凈。這意味著React應(yīng)該通過為類實(shí)現(xiàn)相同的API改進(jìn)來做得更好。

因此,在提出有關(guān)動(dòng)機(jī)的問題之后,讓我們看一下我不喜歡的有關(guān)Funclass的其他內(nèi)容。

隱藏的副作用

在Funclasses的 useEffect 實(shí)現(xiàn)中,最讓我困擾的一件事,就是沒有弄清楚某個(gè)組件的副作用是什么。對(duì)于類,如果你想知道一個(gè)組件在掛載時(shí)做了什么,你可以很容易地檢查 componentDidMount 中的代碼或檢查構(gòu)造函數(shù)。如果你看到一個(gè)重復(fù)的調(diào)用,你可能應(yīng)該檢查一下 componentDidUpdate,有了新的 useEffect 鉤子,副作用可以深深地嵌套在代碼中。

假設(shè)我們檢測(cè)到一些不必要的服務(wù)器調(diào)用,我們查看可疑組件的代碼,然后看到以下內(nèi)容:

const renderContacts = (props) => {
  const [contacts, loadMoreContacts] = useContacts(props.contactsIds);
  return (
    <SmartContactList contacts={contacts}/>
  )
}

這里沒什么特別的,我們應(yīng)該研究 SmartContactList,還是應(yīng)該深入研究 useContacts?讓我們深入研究一下 useContacts 吧:

export const useContacts = (contactsIds) => {
  const {loadedContacts, loadingStatus}  = useContactsLoader();
  const {isRefreshing, handleSwipe} = useSwipeToReresh(loadingStatus);
  // ... many other useX() functions
  useEffect(() => {
    //** 很多代碼,都與一些加載聯(lián)系人的動(dòng)畫有關(guān)。*//
  
  }, [loadingStatus]);
  
  //..rest of code
}

好的,開始變得棘手。隱藏的副作用在哪里?如果我們深入研究 useSwipeToRefresh,我們將看到:

export const useSwipeToRefresh = (loadingStatus) => {
  // ..lot's of code
  // ...
  
  useEffect(() => {
    if(loadingStatus === 'refresing') {
       refreshContacts(); // bingo! 我們隱藏的副作用
    }  
  }); //<== 我們忘記了依賴項(xiàng)數(shù)組!
}

我們發(fā)現(xiàn)了我們的隱藏效果,refreshContacts 會(huì)在每個(gè)組件渲染時(shí)意外地調(diào)用fetch contacts。在大型代碼庫和某些結(jié)構(gòu)不良的組件中,嵌套的 useEffect 可能會(huì)造成麻煩。

我并不是說你不能用類編寫糟糕的代碼,但是Funclasses更容易出錯(cuò),而且沒有嚴(yán)格定義生命周期方法的結(jié)構(gòu),更容易做糟糕的事情。

膨脹的API

通過在類的同時(shí)增加鉤子API,React的API實(shí)際上增加了一倍。現(xiàn)在每個(gè)人都需要學(xué)習(xí)兩種完全不同的方法,我必須說,新API比舊API晦澀得多。一些簡單的事情,如獲得之前的props和state,現(xiàn)在都成了很好的面試材料。你能寫一個(gè)鉤子獲得之前得 props 在不借助google的情況下?

像React這樣的大型庫必須非常小心地在API中添加如此巨大的更改,這樣做的動(dòng)機(jī)甚至是不合理的。

缺乏說明性

在我看來,F(xiàn)unclass比類更混亂。例如,要找到組件的切入點(diǎn)就比較困難——用classes只需搜索 render 函數(shù),但用Funclasses就很難發(fā)現(xiàn)主return語句。另外,要按照不同的 useEffect 語句來理解組件的流程是比較困難的,相比之下,常規(guī)的生命周期方法會(huì)給你一些很好的提示,讓你知道自己的代碼需要在哪里尋找。如果我正在尋找某種初始化邏輯,我將跳轉(zhuǎn)(VSCode中的cmd + shift + o)到 componentDidMount,如果我正在尋找某種更新機(jī)制,則可能會(huì)跳到 componentDidUpdate 等。通過Funclass,我發(fā)現(xiàn)很難在大型組件內(nèi)部定位。

約定驅(qū)動(dòng)的API

鉤子的主要規(guī)則(可能也是最重要的規(guī)則)之一是使用前綴約定。

就是感覺不對(duì)

你知道有什么不對(duì)勁的感覺嗎?這就是我對(duì)鉤子的感覺。有時(shí)我能準(zhǔn)確地指出問題所在,但有時(shí)只是一種普遍的感覺,即我們走錯(cuò)了方向。當(dāng)你發(fā)現(xiàn)一個(gè)好的概念時(shí),你可以看到事情是如何很好地結(jié)合在一起的,但是當(dāng)你在為錯(cuò)誤的概念而苦惱的時(shí)候,發(fā)現(xiàn)你需要添加更多更具體的東西和規(guī)則,才能讓事情順利進(jìn)行。

有了鉤子,就會(huì)有越來越多奇怪的東西跳出來,有更多“有用的”鉤子可以幫助你做一些瑣碎的事情,也有更多的東西需要學(xué)習(xí)。如果我們需要這么多的utils在我們的日常工作中,只是為了隱藏一些奇怪的復(fù)雜,這是一個(gè)巨大的跡象,說明我們走錯(cuò)了路。

幾年前,當(dāng)我從Angular 1.5轉(zhuǎn)到React時(shí),我驚訝于React的API是如此簡單,文檔是如此的薄。Angular曾經(jīng)有龐大的文檔,你可能要花上幾天的時(shí)間才能涵蓋所有內(nèi)容——消化機(jī)制、不同的編譯階段、transclude、綁定、模板等等。光是這一點(diǎn)就給我很大的啟示,而React它簡潔明了,你可以在幾個(gè)小時(shí)內(nèi)把整個(gè)文檔看一遍就可以了。在第一次,第二次以及以后的所有次嘗試使用鉤子的過程中,我發(fā)現(xiàn)自己有義務(wù)一次又一次地使用文檔。

總結(jié)

我討厭成為聚會(huì)的掃興者,但我真的認(rèn)為Hooks可能是React社區(qū)發(fā)生的第2件最糟糕的事情(第一名仍然由Redux占據(jù))。它給已經(jīng)脆弱的生態(tài)系統(tǒng)增加了另一場毫無用處的爭論,目前尚不清楚鉤子是否是推薦的使用方式,還是只是另一個(gè)功能和個(gè)人品味的問題。

我希望React社區(qū)能夠醒來,并要求在Funclass和class的功能之間保持平衡。我們可以在類中擁有更好的Context API,并且可以為類提供諸如useEffect之類的東西。如果需要,React應(yīng)該讓我們選擇繼續(xù)使用類,而不是通過僅為Funclass添加更多功能而強(qiáng)行殺死它而將類拋在后面。

另外,早在2017年底,我就曾以《Redux的丑陋面》為題發(fā)表過一篇文章,如今連Redux的創(chuàng)造者Dan Abramov都已經(jīng)承認(rèn)Redux是一個(gè)巨大的錯(cuò)誤。

React Hooks的丑陋一面

 

只是歷史在重演嗎?時(shí)間會(huì)證明一切。

無論如何,我和我的隊(duì)友決定暫時(shí)堅(jiān)持用類,并使用基于Mobx的解決方案作為狀態(tài)管理工具。我認(rèn)為,在獨(dú)立開發(fā)人員和團(tuán)隊(duì)工作人員之間,Hooks的普及率存在很大差異——Hooks的不良性質(zhì)在大型代碼庫中更加明顯,你需要在該代碼庫中處理其他人的代碼。我個(gè)人真的希望React能把 ctrl+z 的鉤子全部放在一起。

我打算開始著手制定一個(gè)RFC,為React提出一個(gè)簡單、干凈、內(nèi)置的狀態(tài)管理方案,一勞永逸地解決共享狀態(tài)邏輯的問題,希望能用一種比Funclasses不那么笨拙的方式。

分享到:
標(biāo)簽:React Hooks
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績?cè)u(píng)定