<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【React】1009- 換個角度思考 React Hooks

          共 22303字,需瀏覽 45分鐘

           ·

          2021-07-10 00:25

          從 Vue 遷移到 React ,不太習(xí)慣 React Hooks 的使用?也許換個角度思考 Hooks 出現(xiàn)的意義會對你有所幫助。

          1 什么是 Hooks

          簡而言之, Hooks 是個函數(shù),通過使用 Hooks 可以讓函數(shù)組件功能更加豐富。

          在某些場景下,使用 Hooks 是一個比使用類組件更好的主意。

          1.1 Hooks 出現(xiàn)的背景

          在 Hooks 出現(xiàn)之前,函數(shù)組件對比類組件(class)形式有很多局限,例如:

          1. 不能使用 state、ref 等屬性,只能通過函數(shù)傳參的方式使用 props
          2. 沒有生命周期鉤子

          同時在類組件的使用中,也存在著不少難以解決的問題:

          1. 在復(fù)雜組件中,耦合的邏輯代碼很難分離

            組件化講究的是分離邏輯與 UI,但是對于平常所寫的業(yè)務(wù)代碼,較難做到分離和組合。尤其是在生命周期鉤子中,多個不相關(guān)的業(yè)務(wù)代碼被迫放在一個生命周期鉤子中,需要把相互關(guān)聯(lián)的部分拆封更小的函數(shù)。

          2. 監(jiān)聽清理和資源釋放問題

            當組件要銷毀時,很多情況下都需要清除注冊的監(jiān)聽事件、釋放申請的資源。

            事件監(jiān)聽、資源申請需要在 Mount 鉤子中申請,當組件銷毀時還必須在 Unmount 勾子中進行清理,這樣寫使得同一資源的生成和銷毀邏輯不在一起,因為生命周期被迫劃分成兩個部分。

          3. 組件間邏輯復(fù)用困難

            在 React 中實現(xiàn)邏輯復(fù)用是比較困難的。雖然有例如 render props、高階組件等方案,但仍然需要重新組織組件結(jié)構(gòu),不算真正意義上的復(fù)用。抽象復(fù)用一個復(fù)雜組件更是不小的挑戰(zhàn),大量抽象層代碼帶來的嵌套地獄會給開發(fā)者帶來巨大的維護成本。

          4. class 學(xué)習(xí)成本

            與 Vue 的易于上手不同,開發(fā) React 的類組件需要比較扎實的 JavaScript 基礎(chǔ),尤其是關(guān)于 this 、閉包、綁定事件處理器等相關(guān)概念的理解。

          Hooks 的出現(xiàn),使得上述問題得到了不同程度的解決。

          我認為了解 Hooks 出現(xiàn)的背景十分重要。只有知道了為什么要使用 Hooks,知道其所能解決而 class 不能解決的問題時,才能真正理解 Hooks 的思想,真正享受 Hooks 帶來的便利,真正優(yōu)雅地使用 Hooks。

          2 Hooks 基礎(chǔ)

          讓我們從最簡單的 Hooks 使用開始。

          2.1 useState

          這里貼上 React 文檔中的示例:

          import React, { useState } from 'react';

          function Example({
            // 聲明一個 "count" 的 state 變量
            const [count, setCount] = useState(0);

            return (
              <div>
                <p>You clicked {count} times</p>
                <button onClick={() => setCount(count + 1)}>
                  Click me
                </button>
              </div>

            );
          }

          useState 就是一個 Hooks,以前的函數(shù)組件是無狀態(tài)的,但是有了 Hooks 后我們可以在函數(shù)中通過 useState 來獲取 state 屬性(count)以及修改 state 屬性的方法(setCount)。

          整個 Hooks 運作過程:

          1. 函數(shù)組件 Example 第一次執(zhí)行函數(shù)時 useState 進行初始化,其傳入的參數(shù) 0 就是 count 的初始值;
          2. 返回的 VDOM 中使用到了 count 屬性,其值為 0
          3. 通過點擊按鈕,觸發(fā) setCount 函數(shù),傳入修改 count的值,然后重新執(zhí)行函數(shù)(就像類組件中重新執(zhí)行 render 函數(shù)一樣);
          4. 第二次及以后執(zhí)行函數(shù)時,依舊通過 useState 來獲取 count 及修改 count 的方法 setCount,只不過不會執(zhí)行 count的初始化,而是使用其上一次 setCount 傳入的值。

          從使用最簡單的 Hooks 我們可以知道。

          • 存儲 “狀態(tài)” 不再使用一個 state 屬性。

            以往都是把所有狀態(tài)全部放到 state 屬性中,而現(xiàn)在有了 Hooks 我們可以按照需求通過調(diào)用多個 useState 來創(chuàng)建多個 state ,這更有助于分離和修改變量。

            const [count, setCount] = useState(0);
            const [visible, setVisible] = useState(false);
            const [dataList, setDataList] = useState([]);

          • setCount 傳入的參數(shù)是直接覆蓋,而 setState 執(zhí)行的是對象的合并處理。

          總之 useState 使用簡單,它為函數(shù)組件帶來了使用 state 的能力。

          2.2 useEffect

          在 Hooks 出現(xiàn)之前函數(shù)組件是不能訪問生命周期鉤子的,所以提供了 useEffect Hooks 來解決鉤子問題,以往的所有生命周期鉤子都被合并成了 useEffect,并且其解決了之前所提的關(guān)于生命周期鉤子的問題。

          2.2.1 實現(xiàn)生命周期鉤子組合

          先舉一個關(guān)于 class 生命周期鉤子問題的例子,這里貼上 React 文檔的示例:

          // Count 計數(shù)組件
          class Example extends React.Component {
            constructor(props) {
              super(props);
              this.state = {
                count0
              };
            }

            componentDidMount() {
              document.title = `你點擊了 ${this.state.count} 次`;
            }
            componentDidUpdate() {
              document.title = `你點擊了 ${this.state.count} 次`;
            }

            render() {
              return (
                <div>
                  <p>You clicked {this.state.count} times</p>
                  <button onClick={() => this.setState({ count: this.state.count + 1 })}>
                    Click me
                  </button>
                </div>

              );
            }
          }

          可以看到當我們在第一次組件掛載(初始化)后以及之后每次更新都需要該操作,一個是初始化一個是更新后,這種情況在平時經(jīng)常會遇到,有時候遇到初始化問題,就避免不了會寫兩次,哪怕是抽離成單獨的函數(shù),也必須要在兩個地方調(diào)用,當這種寫法多了起來后將會變得冗余且容易出 bug 。

          useEffect 是怎么解決的?一個簡單示例:

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

          function Example() {
            const [count, setCount] = useState(0);

            // 效果如同 componentDidMount 和 componentDidUpdate:
            useEffect(() => {
              // 更新 title
              document.title = `你點擊了 ${count} 次`;
            });

            return (
              <div>
                <p>You clicked {count} times</p>
                <button onClick={() => setCount(count + 1)}>
                  Click me
                </button>
              </div>

            );
          }

          它把兩個生命周期鉤子合并在了一起。

          整個 Hooks 過程:

          1. Example 組件第一次執(zhí)行時,返回 VDOM,渲染;
          2. 渲染后從上至下按順序執(zhí)行 useEffect
          3. Example 組件更新后,返回 VDOM,渲染;
          4. 渲染后從上至下按順序執(zhí)行 useEffect

          可以看到無論是初始化渲染還是更新渲染,useEffect 總是會確保在組件渲染完畢后再執(zhí)行,這就相當于組合了初始化和更新渲染時的生命周期鉤子。并且由于閉包的特性,useEffect 可以訪問到函數(shù)組件中的各種屬性和方法。

          useEffect 里面可以進行 “副作用” 操作,例如:

          1. 更變 DOM(調(diào)用 setCount)
          2. 發(fā)送網(wǎng)絡(luò)請求
          3. 掛載監(jiān)聽

          不應(yīng)該把 “副作用” 操作放到函數(shù)組件主體中,就像不應(yīng)該把 “副作用” 操作放到 render 函數(shù)中一樣,否則很可能會導(dǎo)致函數(shù)執(zhí)行死循環(huán)或資源浪費等問題。

          2.2.2 實現(xiàn)銷毀鉤子

          這就完了嗎?沒有,對于組件來說,有些其內(nèi)部是有訂閱外部數(shù)據(jù)源的,這些訂閱的 “副作用” 如果在組件卸載時沒有進行清除,將會容易導(dǎo)致內(nèi)存泄漏。React 類組件中還有個非常重要的生命周期鉤子 componentWillUnmount,其在組件將要銷毀時執(zhí)行。

          下面演示類組件是如何清除訂閱的:

          // 一個訂閱好友的在線狀態(tài)的組件
          class FriendStatus extends React.Component {
            constructor(props) {
              super(props);
              this.state = { isOnlinenull };
              this.handleStatusChange = this.handleStatusChange.bind(this);
            }
            
           // 初始化:訂閱好友在線狀態(tài)
            componentDidMount() {
              ChatAPI.subscribeToFriendStatus(
                this.props.friend.id,
                this.handleStatusChange,
              );
            }

            // 更新:好友訂閱更改
            componentDidUpdate(prevProps) {
              // 如果 id 相同則忽略
              if (prevProps.friend.id === this.props.friend.id) {
                return;
              }
              // 否則清除訂閱并添加新的訂閱
              ChatAPI.unsubscribeFromFriendStatus(
                prevProps.friend.id,
                this.handleStatusChange,
              );
              ChatAPI.subscribeToFriendStatus(
                this.props.friend.id,
                this.handleStatusChange,
              );
            }

            // 銷毀:清除好友訂閱
            componentWillUnmount() {
              ChatAPI.unsubscribeFromFriendStatus(
                this.props.friend.id,
                this.handleStatusChange,
              );
            }
            
            // 訂閱方法
            handleStatusChange(status) {
              this.setState({
                isOnline: status.isOnline,
              });
            }

            render() {
              if (this.state.isOnline === null) {
                return 'Loading...';
              }
              return this.state.isOnline ? 'Online' : 'Offline';
            }
          }

          可以看到,一個好友狀態(tài)訂閱使用了三個生命周期鉤子。

          那么使用 useEffect 該如何實現(xiàn)?

          function FriendStatus(props{
            const [isOnline, setIsOnline] = useState(null);

            useEffect(() => {
              function handleStatusChange(status{
                setIsOnline(status.isOnline);
              }
              ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
              
              // 清除好友訂閱
              return function cleanup() {
                ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
              };
            });

            if (isOnline === null) {
              return 'Loading...';
            }
            return isOnline ? 'Online' : 'Offline';
          }

          useEffect 把好友訂閱相關(guān)的邏輯代碼組合到了一起,而不像類組件那樣把同一類型的邏輯代碼按照生命周期來劃分。

          其中 return 的函數(shù)是在 useEffect 再次執(zhí)行前或是組件要銷毀時執(zhí)行,由于閉包,useEffect 中的返回函數(shù)可以很容易地獲取對象并清除訂閱。

          整個 Hooks 過程:

          1. 初始化函數(shù)組件 FriendStatus,掛載 VDOM;
          2. 按順序執(zhí)行 useEffect 中傳入的函數(shù);
          3. 更新:函數(shù) FriendStatus 重新執(zhí)行,重新掛載 VDOM;
          4. 執(zhí)行上一次 useEffect 傳入函數(shù)的返回值:清除好友訂閱的函數(shù);
          5. 執(zhí)行本次 useEffect 中傳入的函數(shù)。

          2.2.3 實現(xiàn)不同邏輯分離

          剛才講的都是在一個場景下使用 Hooks 。

          現(xiàn)在將計數(shù)組件和好友在線狀態(tài)組件結(jié)合并作對比。

          class FriendStatusWithCounter extends React.Component {
            constructor(props) {
              super(props);
              this.state = { count0isOnlinenull };
              this.handleStatusChange = this.handleStatusChange.bind(this);
            }

            componentDidMount() {
              document.title = `你點擊了 ${count} 次`;
              ChatAPI.subscribeToFriendStatus(
                this.props.friend.id,
                this.handleStatusChange
              );
            }

            componentDidUpdate() {
              document.title = `你點擊了 ${count} 次`;
            }

            componentWillUnmount() {
              ChatAPI.unsubscribeFromFriendStatus(
                this.props.friend.id,
                this.handleStatusChange
              );
            }
            
            componentDidUpdate(prevProps) {
              // 如果 id 相同則忽略
              if (prevProps.friend.id === this.props.friend.id) {
                return;
              }
              // 否則清除訂閱并添加新的訂閱
              ChatAPI.unsubscribeFromFriendStatus(
                prevProps.friend.id,
                this.handleStatusChange,
              );
              ChatAPI.subscribeToFriendStatus(
                this.props.friend.id,
                this.handleStatusChange,
              );
            }

            handleStatusChange(status) {
              this.setState({
                isOnline: status.isOnline
              });
            }

          可以很明顯地感受到,在多個生命周期鉤子中,計數(shù)和好友訂閱等邏輯代碼都混合在了同一個函數(shù)中。

          接下來看看 useEffect 是怎么做的:

          function FriendStatusWithCounter(props{
            // 計數(shù)相關(guān)代碼
            const [count, setCount] = useState(0);
            useEffect(() => {
              document.title = `你點擊了 ${count} 次`;
            });

            // 好友訂閱相關(guān)代碼
            const [isOnline, setIsOnline] = useState(null);
            useEffect(() => {
              function handleStatusChange(status{
                setIsOnline(status.isOnline);
              }

              ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
              return () => {
                ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
              };
            });
            // ...
          }

          useEffect 可以像使用多個 useState 那樣,把組件的邏輯代碼進行分離和組合,更有利于組件的開發(fā)和維護。

          2.2.4 跳過 useEffect

          有些時候并沒有必要每次在函數(shù)組件重新執(zhí)行時執(zhí)行 useEffect,這個時候就需要用到 useEffect 的第二個參數(shù)了。

          第二個參數(shù)傳入一個數(shù)組,數(shù)組元素是要監(jiān)聽的變量,當函數(shù)再次執(zhí)行時,數(shù)組中只要有一個元素與上次函數(shù)執(zhí)行時傳入的數(shù)組元素不同,那么則執(zhí)行 useEffect 傳入的函數(shù),否則不執(zhí)行。

          給個示例會更好理解:

          function FriendStatus(props{
            const [isOnline, setIsOnline] = useState(null);

            useEffect(() => {
              function handleStatusChange(status{
                setIsOnline(status.isOnline);
              }
              ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
              
              // 清除好友訂閱
              return function cleanup({
                ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
              };
              
              // 加入 props.friend.id 作為依賴,當 id 改變時才會執(zhí)行該次 useEffect
            }, [props.friend.id]);

            if (isOnline === null) {
              return 'Loading...';
            }
            return isOnline ? 'Online' : 'Offline';
          }

          useEffect 加入 id 的依賴,只有當 id 改變時,才會再次清除、添加訂閱,而不必每次函數(shù)重新執(zhí)行時都會清除并添加訂閱。

          需要注意的是,對于傳入的對象類型,React 只是比較引用是否改變,而不會判斷對象的屬性是否改變,所以建議依賴數(shù)組中傳入的變量都采用基本類型。

          3 真正的 Hooks

          剛才只是 Hooks 的簡單使用,但是會使用并不能代表著真正理解到了 Hooks 的思想。

          從類組件到函數(shù)組件不僅僅是使用 Hooks 的區(qū)別,更重要的是開發(fā)時根本上思維模式的變化

          讓我們換個角度思考。

          3.1 useEffect —— 遠不止生命周期

          很多人認為 useEffect 只是生命周期鉤子的更好替代品,這是不完全正確的。

          試想一下這樣的場景:一個圖表組件 Chart 需要接收大量的數(shù)據(jù)然后對其進行大量計算處理(getDataWithinRange())并做展示。

          類組件:

          // 大量計算處理
          function getDataWithinRange({
            //...
          }

          class Chart extends Component {
            state = {
              datanull,
            }
            componentDidMount() {
              const newData = getDataWithinRange(this.props.dateRange)
              this.setState({data: newData})
            }
            componentDidUpdate(prevProps) {
              if (prevProps.dateRange != this.props.dateRange) {
                const newData = getDataWithinRange(this.props.dateRange)
                this.setState({data: newData})
              }
            }
            render() {
              return (
                <svg className="Chart" />
              )
            }
          }

          當使用生命周期鉤子時,我們需要手動去判斷哪些數(shù)據(jù)(dataRange)發(fā)生了變化,然后更新到對應(yīng)的數(shù)據(jù)(data)。

          而在 Hooks 的使用中,我們只需關(guān)注哪些值(dataRange)需要進行同步

          使用 useEffect 的函數(shù)組件:

          const Chart = ({ dateRange }) => {
            const [data, setData] = useState()
            
            useEffect(() => {
              const newData = getDataWithinRange(dateRange)
              setData(newData)
            }, [dateRange])
            
            return (
              <svg className="Chart" />
            )
          }

          useEffect 可以讓你有更簡單的想法實現(xiàn)保持變量同步

          不過這還不夠簡單,我們可以再看下一個例子。

          3.2 強大的 useMemo

          事實上,剛才 Hooks 中的例子還是有些類組件的思維模式,顯得有些復(fù)雜了。

          1. 使用 useEffect 進行數(shù)據(jù)的處理;
          2. 存儲變量到 state
          3. 在 JSX 中引用 state

          有沒有發(fā)現(xiàn)中間多了個 state 的環(huán)節(jié)?

          我們不需要使用 state ,那是類組件的開發(fā)模式,因為在類組件中,render 函數(shù)和生命周期鉤子并不是在同一個函數(shù)作用域下執(zhí)行,所以需要 state 進行中間的存儲,同時執(zhí)行的 setStaterender 函數(shù)再次執(zhí)行,借此獲取最新的 state

          而在函數(shù)式組件中我們有時根本不會需要用到 state 這樣的狀態(tài)存儲,我們僅僅是想使用

          所以我們可以把剛才的圖表例子寫成這樣:

          const Chart = ({ dateRange }) => {
            
            const data = useMemo(() => (
              getDataWithinRange(dateRange)
            ), [dateRange])
            
            return (
              <svg className="Chart" />
            )
          }

          useMemo 會返回一個“記憶化”的結(jié)果,執(zhí)行當前傳入的函數(shù)并返回結(jié)果值給聲明的變量,且當依賴沒變化時返回上一次計算的值。

          為什么可以這樣寫?

          因為函數(shù)組件中 render 和生命周期鉤子在同一個函數(shù)作用域中,這也就意味著不再需要 state 作中間數(shù)據(jù)橋梁,我們可以直接在函數(shù)執(zhí)行時獲取到處理的數(shù)據(jù),然后在 return 的 JSX 中使用,不必需要每次使用屬性都要在 state 中聲明和創(chuàng)建了,不再需要重新渲染執(zhí)行一次函數(shù)(setData)了,所以我們?nèi)コ袅?useState。這樣,我就減少了一個 state 的聲明以及一次重新渲染

          我們把變量定義在函數(shù)里面,而不是定義在 state 中,這是類組件由于其結(jié)構(gòu)和作用域上與函數(shù)組件相比的不足,是函數(shù)組件的優(yōu)越性。

          當然,如果 getDataWithinRange 函數(shù)開銷不大的話,這樣寫也是可以的:

          const Chart = ({ dateRange }) => {
            const newData = getDataWithinRange(dateRange)
            return (
              <svg className="Chart" />
            )
          }

          函數(shù)上下文中進行數(shù)據(jù)的處理和使用,是類結(jié)構(gòu)組件所難以實現(xiàn)的。

          如果還沒有體會到 Hooks 所帶來的變化,那么下面的例子可能會令你有所領(lǐng)悟。

          3.3 多個數(shù)據(jù)依賴

          上一個例子我們只要處理一個數(shù)據(jù)就可以了,這次我們嘗試處理多條數(shù)據(jù),并且數(shù)據(jù)間有依賴關(guān)系。

          需求如下:

          1. 需要對傳入的 dataRange 進行處理得到 data
          2. margins 改變后需要更新 dimensions
          3. data 改變后需要更新 scales

          類組件:

          class Chart extends Component {
            state = {
              datanull,
              dimensionsnull,
              xScalenull,
              yScalenull,
            }
            componentDidMount() {
              const newData = getDataWithinRange(this.props.dateRange)
              this.setState({data: newData})
              this.setState({dimensions: getDimensions()})
              this.setState({xScale: getXScale()})
              this.setState({yScale: getYScale()})
            }
            componentDidUpdate(prevProps, prevState) {
              if (prevProps.dateRange != this.props.dateRange) {
                const newData = getDataWithinRange(this.props.dateRange)
                this.setState({data: newData})
              }
              if (prevProps.margins != this.props.margins) {
                this.setState({dimensions: getDimensions()})
              }
              if (prevState.data != this.state.data) {
                this.setState({xScale: getXScale()})
                this.setState({yScale: getYScale()})
              }
            }
            render() {
              return (
                <svg className="Chart" />
              )
            }
          }

          函數(shù)組件:

          const Chart = ({ dateRange, margins }) => {
            const data = useMemo(() => (
              getDataWithinRange(dateRange)
            ), [dateRange])
            const dimensions = useMemo(getDimensions, [margins])
            const xScale = useMemo(getXScale, [data])
            const yScale = useMemo(getYScale, [data])
            return (
              <svg className="Chart" />
            )
          }

          為什么代碼那么少?因為在 Hooks 中我們依舊只需關(guān)注哪些值(data、dimensions、xScale、yScale)需要同步即可。

          而觀察類組件的代碼,我們可以發(fā)現(xiàn)其使用了大量的陳述性代碼,例如判斷是否相等,同時還使用了 state 作為數(shù)據(jù)的存儲和使用,所以產(chǎn)生了很多 setState 代碼以及增加了多次重新渲染。

          3.4 解放 State

          還是剛才 3.3 的例子,不過把需求稍微改了一下:讓 scales 依賴于 dimensions

          看看類組件是如何做到的:

          class Chart extends Component {
            state = {
              datanull,
              dimensionsnull,
              xScalenull,
              yScalenull,
            }
            componentDidMount() {
              const newData = getDataWithinRange(this.props.dateRange)
              this.setState({data: newData})
              this.setState({dimensions: getDimensions()})
              this.setState({xScale: getXScale()})
              this.setState({yScale: getYScale()})
            }
            componentDidUpdate(prevProps, prevState) {
              if (prevProps.dateRange != this.props.dateRange) {
                const newData = getDataWithinRange(this.props.dateRange)
                this.setState({data: newData})
              }
              if (prevProps.margins != this.props.margins) {
                this.setState({dimensions: getDimensions()})
              }
              if (
                prevState.data != this.state.data
                || prevState.dimensions != this.state.dimensions
              ) {
                this.setState({xScale: getXScale()})
                this.setState({yScale: getYScale()})
              }
            }
            render() {
              return (
                <svg className="Chart" />
              )
            }
          }

          由于依賴關(guān)系發(fā)生了變化,所以需要重新進行判斷,并且由于多個依賴關(guān)系,判斷的條件也變得更加復(fù)雜了,代碼的可讀性也大幅降低。

          接著看 Hooks 是如何做到的:

          const Chart = ({ dateRange, margins }) => {
            const data = useMemo(() => (
              getDataWithinRange(dateRange)
            ), [dateRange])
            const dimensions = useMemo(getDimensions, [margins])
            const xScale = useMemo(getXScale, [data, dimensions])
            const yScale = useMemo(getYScale, [data, dimensions])
            return (
              <svg className="Chart" />
            )
          }

          使用 Hooks 所以不用再去關(guān)心誰是 props 誰是 state,不用關(guān)心該如何存儲變量,存儲什么變量等問題,也不必去關(guān)心如何進行判斷的依賴關(guān)系。在 Hooks 開發(fā)中,我們把這些瑣碎的負擔都清除了,只需關(guān)注要同步的變量。

          所以當數(shù)據(jù)關(guān)系復(fù)雜起來的時候,類組件的這種寫法顯得比較笨重,使用 Hooks 的優(yōu)勢也就體現(xiàn)出來了。

          再回顧一下之前一步步走過來的示例,可以看到 Hooks 幫我們精簡了非常多的代碼。

          代碼越短并不意味著可讀性越好,但是更加精簡、輕巧的組件,更容易讓我們把關(guān)注點放在更有用的邏輯上,而不是把精力消耗在判斷依賴的冗余編碼中。

          4 參考文章


          1. JavaScript 重溫系列(22篇全)
          2. ECMAScript 重溫系列(10篇全)
          3. JavaScript設(shè)計模式 重溫系列(9篇全)
          4. 正則 / 框架 / 算法等 重溫系列(16篇全)
          5. Webpack4 入門(上)|| Webpack4 入門(下)
          6. MobX 入門(上) ||  MobX 入門(下)
          7. 120+篇原創(chuàng)系列匯總

          回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

          點擊“閱讀原文”查看 120+ 篇原創(chuàng)文章

          瀏覽 23
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  韩国无码不卡 | 丁香五月激情婷婷 | 免费观看日韩a | 久久中文综合网 | 青娱乐国产精品 |