91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

掌握Hooks的方法是什么

發布時間:2021-10-23 10:12:26 來源:億速云 閱讀:128 作者:iii 欄目:web開發

這篇文章主要介紹“掌握Hooks的方法是什么”,在日常操作中,相信很多人在掌握Hooks的方法是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”掌握Hooks的方法是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

在使用 React 開發的這段時間里,我最大的感受就是 “這是 React 最好的時代,也是最壞的時代” !「好」在于 hooks  開啟了不一樣的開發模式,在思考方式上要求更關注于數據之間的依賴關系,同時書寫方式更加簡便,總體上提升了開發效率;「壞」在于項目中經常是類組件與函數組件共存,而類組件以類編程思想為主導,開發過程中更關注于整個組件的渲染周期,在維護項目時常常需要在兩種思維模式中左右橫跳,這還不是最壞的一點。

某日,老王問我:“你一直在「每周一瞥」搬運 hooks 的文章,你覺得 hooks 有哪些易造成內存泄露的點?”  引發了我的深思(因為我的腦子一片空白)。我們一直在討論 hooks,到底在討論什么?雖然社區內關于 hooks 的討論很多,但更多的是科普 Hooks API  怎么使用,亦或是將其與類組件生命周期、redux 進行對比,而缺少關于 hooks 最佳實踐的討論與共識,我想這才是「最壞」的一點。今天,我們不妨討論一下  hooks 所帶來的變化以及我們如何去擁抱這些變化。

注「每周一瞥」是團隊內翻譯并分享外網新鮮貨的一個專欄。

掌握Hooks的方法是什么

React 16.8 發布以來,Hooks 深入人心,帶來最大的變化有三點:思維模式的轉變,渲染過程中作用域的變化以及數據流的改變。

思維模式

從 React 官網可以了解到,Hooks 的設計動機在于簡化組件間狀態邏輯的復用,支持開發者將關聯的邏輯抽象為更小的函數,并降低認知成本,不用去理解  JS Class 中令人窒息的 this。在這樣的動機之下,hooks  中弱化了組件生命周期的概念,強化了狀態與行為之間的依賴關系,這容易引導我們更多的關注“做什么”,而非“怎么做”[1]。

假設有這么一個場景:組件 Detail 中依賴父級組件傳入的 query 參數進行數據請求,那么無論是基于類組件還是  Hooks,我們都需要定義一個異步請求方法  getData。不同的是,在類組件的開發模式中,我們要思考的更傾向于“怎么做”:在組件掛載完成時請求數據,并在組件發生更新時,比較新舊 query  值,必要時重新調用 getData 函數。

class Detail extends React.Component {   state = {     keyword: '',   }    componentDidMount() {     this.getData();   }    getSnapshotBeforeUpdate(prevProps, prevState) {     if (this.props.query !== prevProps.query) {       return true;     }     return null;   }    componentDidUpdate(prevProps, prevState, snapshot) {     if (snapshot) {       this.getData();     }   }    async getData() {     // 這是一段異步請求數據的代碼     console.log(`數據請求了,參數為:${this.props.query}`);     this.setState({       keyword: this.props.query     })   }    render() {     return (       <div>         <p>關鍵詞: {this.state.keyword}</p>       </div>     );   } }

而在應用了 Hooks 的函數組件中,我們思考“做什么”:不同 query 值,展示不同的數據。

function Detail({   query }) {   const [keyword, setKeyword] = useState('');    useEffect(() => {     const getData = async () => {       console.log(`數據請求了,參數為:${query}`);       setKeyword(query);     }      getData();   }, [query]);    return (     <div>       <p>關鍵詞: {keyword}</p>     </div>   ); }

在這種主導下,開發者在編碼過程中的思維模式也應隨之改變,需要考慮數據與數據、數據與行為之間的同步關系。這種模式可以更簡潔地將相關代碼組合到一起,甚至抽象成自定義  hooks,實現邏輯的共享,似乎有了插拔式編程的味道。

雖然 Dan Abramov 在自己的博客中提到,從生命周期的角度思考并決定何時執行副作用是在逆勢而為[2],但是了解各個 hooks  在組件渲染過程中的執行時機,有助于我們與 React 保持理解的一致性,能夠更加準確地專注于“做什么”。 Donavon 以圖表形式梳理對比了 hooks  范式與生命周期范式[3],能夠幫助我們理解 hooks  在組件中的工作機制。每次組件發生更新時,都會重新調用組件函數,生成新的作用域,這種變化也對我們開發者提出了新的編碼要求。

掌握Hooks的方法是什么

作用域

在類組件中,組件一旦實例化后,便有了自己的作用域,從創建到銷毀,作用域始終不變。因此,在整個組件的生命周期中,每次渲染時內部變量始終指向同一個引用,我們可以很輕易的在每次渲染中通過  this.state 拿到最新的狀態值,也可以使用 this.xx 獲取到同一個內部變量。

class Timer extends React.Component {   state = {     count: 0,     interval: null,   }    componentDidMount() {     const interval = setInterval(() => {       this.setState({         count: this.state.count + 1,       })     }, 1000);      this.setState({       interval     });   }    componentDidUnMount() {     if (this.state.interval) {       clearInterval(this.state.interval);     }   }    render() {     return (       <div>         計數器為:{this.state.count}       </div>     );   } }

Hooks 中, render 與 state 的關系更像閉包與局部變量。每次渲染時,都會生成新的 state 變量,React  會向其寫入當次渲染的狀態值,并在當次渲染過程中保持不變。也即每次渲染互相獨立,都有自己的狀態值。同理,組件內的函數、定時器、副作用等也是獨立的,內部所訪問的也是當次渲染的狀態值,因此常常會遇到定時器/訂閱器內無法讀取到最新值的情況。

function Timer() {   const [count, setCount] = useState(0);    useEffect(() => {     const interval = setInterval(() => {       setCount(count + 1);    // 始終只為 1      }, 1000);      return () => {       clearInterval(interval);     }   }, []);    return (     <div>       計數器為:{count}     </div>   ); }

如果我們想要拿到最新值,有兩種解決方法:一是利用 setCount 的 lambada 形式,傳入一個以上一次的狀態值為參數的函數;二是借助 useRef  鉤子,在其 current 屬性中存儲最新的值。

function Timer() {   const [count, setCount] = useState(0);    useEffect(() => {     const interval = setInterval(() => {       setCount(c => c + 1);     }, 1000);      return () => {       clearInterval(interval);     }   }, []);    return (     <div>       計數器為:{count}     </div>   ); }

在 hook-flow  的圖中,我們可以了解到當父組件發生重新渲染時,其所有(狀態、局部變量等)都是新的。一旦子組件依賴于父組件的某一個對象變量,那么無論對象是否發生變化,子組件拿到的都是新的對象,從而使子組件對應的  diff 失效,依舊會重新執行該部分邏輯。在下面的例子中,我們的副作用依賴項中包含了父組件傳入的對象參數,每次父組件發生更新時,都會觸發數據請求。

function Info({   style, }) {   console.log('Info 發生渲染');    useEffect(() => {     console.log('重新加載數據'); // 每次發生重新渲染時,都會重新加載數據   }, [style]);    return (     <p style={style}>       這是 Info 里的文字     </p>   ); }  function Page() {   console.log('Page 發生渲染');    const [count, setCount] = useState(0);   const style = { color: 'red' };    // 計數器 +1 時,引發 Page 的重新渲染,進而引發 Info 的重新渲染   return (     <div>       <h5>計數值為:{count}</h5>       <button onClick={() => setCount(count + 1)}> +1 </button>       <Info style={style} />     </div>   ); }

React Hooks 給我們提供了解決方案,useMemo 允許我們緩存傳入的對象,僅當依賴項發生變化時,才重新計算并更新相應的對象。

function Page() {   console.log('Page 發生渲染');    const [color] = useState('red');   const [count, setCount] = useState(0);   const style = useMemo(() => ({ color }), [color]); // 只有 color 發生實質性改變時,style 才會變化    // 計數器 +1 時,引發 Page 的重新渲染,進而引發 Info 的重新渲染   // 但是由于 style 緩存了,因此不會觸發 Info 內的數據重新加載   return (     <div>       <h5>計數值為:{count}</h5>       <button onClick={() => setCount(count + 1)}> +1 </button>       <Info style={style} />     </div>   ); }

數據流

React Hooks 在數據流上帶來的變化有兩點:一是支持更友好的使用 context  進行狀態管理,避免層級過多時向中間層承載無關參數;二是允許函數參與到數據流中,避免向下層組件傳入多余的參數。

useContext 作為 hooks 的核心模塊之一,可以獲取到傳入 context 的當前值,以此達到跨層通信的目的。React  官網有著詳細的介紹,需要關注的是一旦 context 值發生改變,所有使用了該 context 的組件都會重新渲染。為了避免無關的組件重繪,我們需要合理的構建  context ,比如從第一節提到的新思維模式出發,按狀態的相關度組織 context,將相關狀態存儲在同一個 context 中。

在過去,如果父子組件用到同一個數據請求方法 getData ,而該方法又依賴于上層傳入的 query 值時,通常需要將 query 和 getData  方法一起傳遞給子組件,子組件通過判斷 query 值來決定是否重新執行 getData。

class Parent extends React.Component {    state = {     query: 'keyword',   }    getData() {     const url = `https://mocks.alibaba-inc.com/mock/fO87jdfKqX/demo/queryData.json?query=${this.state.query}`;     // 請求數據...     console.log(`請求路徑為:${url}`);   }    render() {     return (       // 傳遞了一個子組件不渲染的 query 值       <Child getData={this.getData} query={this.state.query} />     );   } }  class Child extends React.Component {   componentDidMount() {     this.props.getData();   }    componentDidUpdate(prevProps) {     // if (prevProps.getData !== this.props.getData) { // 該條件始終為 true     //   this.props.getData();     // }     if (prevProps.query !== this.props.query) { // 只能借助 query 值來做判斷       this.props.getData();     }   }    render() {     return (       // ...     );   } }

在 React Hooks 中 useCallback 支持我們緩存某一函數,當且僅當依賴項發生變化時,才更新該函數。這使得我們可以在子組件中配合  useEffect ,實現按需加載。通過 hooks 的配合,使得函數不再僅僅是一個方法,而是可以作為一個值參與到應用的數據流中。

function Parent() {   const [count, setCount] = useState(0);   const [query, setQuery] = useState('keyword');    const getData = useCallback(() => {     const url = `https://mocks.alibaba-inc.com/mock/fO87jdfKqX/demo/queryData.json?query=${query}`;     // 請求數據...     console.log(`請求路徑為:${url}`);   }, [query]);  // 當且僅當 query 改變時 getData 才更新    // 計數值的變化并不會引起 Child 重新請求數據   return (     <>       <h5>計數值為:{count}</h5>       <button onClick={() => setCount(count + 1)}> +1 </button>       <input onChange={(e) => {setQuery(e.target.value)}} />       <Child getData={getData} />     </>   ); }  function Child({   getData }) {   useEffect(() => {     getData();   }, [getData]);    // 函數可以作為依賴項參與到數據流中    return (     // ...   ); }

總結

回到最初的問題:“ hooks 有哪些易造成內存泄露的點?”,我理解造成內存泄露風險的在于 hooks  所帶來的作用域的變化。由于每次渲染都是獨立的,一旦有副作用引用了局部變量,并且未在組件銷毀時及時釋放,那么就極易造成內存泄露。關于如何更好的使用 hooks,  Sandro Dolidze 在博客中列了一個 checkList[4],我覺得是個不錯的建議,可以幫助我們寫出正確的 hooks 應用。

  1. 遵循 Hooks 規則;

  2. 不要在函數體中使用任何副作用,而是將其放到 useEffect 中執行;

  3. 取消訂閱/處理/銷毀所有已使用的資源;

  4. 首選 useReducer 或 useState 的函數更新,以防止在鉤子中讀寫相同的值;

  5. 不要在 render 函數中使用可變變量,而是使用 useRef;

  6. 如果在 useRef 中保存的內容的生命周期比組件本身小,那么在處理資源時不要釋放該值;

  7. 小心死循環和內存泄露;

  8. 當需要提高性能是,可以 memoize 函數和對象;

  9. 正確設置依賴項(undefined => 每次渲染; [a, b] => 當 a 或 b 改變時;[] => 僅執行一次);

  10. 在可復用用例中使用自定義 hooks.

到此,關于“掌握Hooks的方法是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

凤阳县| 南岸区| 吴堡县| 三门峡市| 丹巴县| 合作市| 西畴县| 贡觉县| 兰考县| 榆社县| 上杭县| 南丰县| 南和县| 永城市| 镇巴县| 灌南县| 广东省| 麻江县| 桃园市| 五河县| 十堰市| 临湘市| 班玛县| 麦盖提县| 满城县| 昌江| 阿拉尔市| 栾城县| 西乌珠穆沁旗| 邛崃市| 望城县| 当雄县| 灵川县| 奉贤区| 巴里| 富锦市| 肃南| 江阴市| 巧家县| 泗阳县| 龙胜|