React Hooks - 如何安全地使用state

Hi,丹尼爾,今天咱們來聊一聊 "如何安全地使用 state" 怎麼樣?react

丹尼爾:安全?難道使用 React Hooks 的 state 還會帶來危險?X﹏X 緊張~

放鬆放鬆,關於安全,等會我給個例子就會很好理解了。沒有例子的輔助,好難一會兒說明白,也多是個人表達能力 So So 吧!git

丹尼爾:好吧。在場的各位同窗,有不清楚 React Hooks 是什麼的嗎?

也許有一些同窗還不太瞭解,我就簡單地介紹下(忽然有種湊字數的嫌疑 ヘ(・_|):github

React Hooks 是個好東西,它讓你能夠用純函數來實現有狀態的組件,今後你不用再糾結組件究竟是有狀態仍是無狀態,從而在純函數和類兩種實現間徘徊(固然好處多多,毫不止這個,後面我會專門寫一篇介紹 Why React Hooks 的文章滴😬)。數組

一旦你踏上 React Hooks 的征途,就會碰到各類各樣奇怪的問題。安全

這很正常,一個新事物的誕生,老是伴隨着各類問題,而後在打怪中不斷升級成長,最終成爲。。。函數

丹尼爾:喂喂,別扯遠了

🤪若是你有看過關於 React Hooks 的官方文檔,看過裏面的一些例子,你可能會以爲,挺簡單的,不過是改爲用 useState 來使用 state 而已,沒啥難度。this

然而就在你放鬆警戒的這個moment,「危險」正在悄悄地在某些角落降臨了。spa

丹尼爾:緊張,什麼危險,不會失身吧

😅 額~~3d

對於 state 值的獲取,你可能取到了一個不是你所指望的舊值,即不是最新的狀態值。code

你須要時時刻刻保持清醒,纔有可能繞過這些「危險」。

丹尼爾:😵

看着你一臉蒙B的樣子,我這就趕忙給個例子哈。

栗子來了:當你在表單上填寫了一些信息,而後離開表單時,但願能自動保存爲草稿,以便下次進來時能還原,省去從新填寫的麻煩。

下面給出簡化版的實現:

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

export const UseStateDemoComp1 = () => {
  const [name, setName] = useState('daniel');

  useEffect(function() {
    return () => {
      saveDraft()
    }
  }, [])

  function saveDraft() {
    console.log('Save Draft:', name);
  }

  return (
    <div>
      <form>
        <input value={name} onChange={e => setName(e.target.value)}></input>
      </form>
    </div>
  );
};

昨一看,代碼好像沒啥問題。

傳給 useEffect 的 function 返回了一個 function,該 function 至關於原來的componentWillUnmount生命週期方法,即只在組件快銷燬的時候纔會調用執行。在該方法裏面執行一些邏輯,這裏調用saveDraft方法,獲取 name state 的值,而後保存。

找出問題了嗎?若是沒有,那咱們直接看個動圖吧,看看問題所在:

動圖演示

點擊 Toggle 按鈕會銷燬掉組件,因此會觸發銷燬動做。從動圖能夠看到,咱們填寫的是「sarah」,但在銷燬執行的方法中,取到的值倒是「daniel」

丹尼爾:這是爲何呢?不該該是最新的值嗎?😶

由於useEffect的依賴是個空數組,因此在整個組件生命週期只會執行一次,即在組件第一次完成渲染後執行,而useEffect的方法裏面用到的 state 值就是當時最新的state值,能夠用快照來理解。在接下來的時間裏,不管組件從新渲染多少次,都不會改變裏面的 state 的值,由於它只是當時的快照

有同窗可能會說,把 name 加進 useEffect 依賴的數組裏面就好了。以下:

useEffect(function() {
    return () => {
      saveDraft()
    }
}, [name])

看上去好像是知足了需求,但依然有問題。由於我只想在組件退出的時候才保存,但如今的效果就是表單字段值一旦改變,就會保存,保存操做變得很是的頻繁。

動圖演示

丹尼爾:因此你想說 React Hooks 對此毫無辦法?

固然不是啦,經過 useRefuseEffect 是能夠實現上面的需求的。怎麼實現?同窗們就本身動下腦筋吧。當你實現出來後,你會發現代碼不只囉嗦,可讀性還差。

丹尼爾:好了,請開始你的表演吧

😎有了上面的例子,這裏就能夠對安全地使用state做個描述了:

「安全地使用 state,就是不管你在什麼時候何地以何種方式讀取 state 的值時,它老是符合你的預期,老是最新的那個值,而不用你當心翼翼地去判斷它會不會是個沒更新的舊值」

官方提供了自定義Hooks的能力,就是想經過社區的共同努力,來持續完善Hooks。

接下來咱們經過 【nice-hooks】這個第三方自定義Hooks的開源項目,使用它的 useSingleState 來代替 useState,話很少說,直接上代碼

import React, { useEffect } from "react";
import { useSingleState } from "nice-hooks";

export const DemoComp1 = () => {
  const [state, setState] = useSingleState({
    name: 'daniel'
  });

  useEffect(function() {
    return () => {
      saveDraft()
    }
  }, [])

  function saveDraft() {
    console.log('Save Draft:', state.name);
  }

  return (
    <div>
      <form>
        <input value={state.name} onChange={e => setState({name: e.target.value})}></input>
      </form>
    </div>
  );
};

先來個動圖直接看下效果。完美~

動圖演示

這裏介紹一下useSingleState這個 hook:它使用相似於 class 形式的 this.statethis.setState 的方式來使用 state,因此很是容易上手。最重要是它能夠安全地使用 state,而且擁有 callback 能力。

最後咱們使用 useLifeCycle 這個 hook 對代碼進一步完善。好了,下面的代碼成品相比於直接使用官方的 hooks,是否是感受好多了。

import React from "react";
import { useSingleState, useLifeCycle } from "nice-hooks";

export const DemoComp1 = () => {
  const [state, setState] = useSingleState({
    name: 'daniel'
  });

  useLifeCycle({
    willUnmount() {
      saveDraft()
    }
  })

  function saveDraft() {
    console.log('Save Draft:', state.name);
  }

  return (
    <div>
      <form>
        <input value={state.name} onChange={e => setState({name: e.target.value})}></input>
      </form>
    </div>
  );
};

時間真快,又到了該說再見的時候。

若是你以爲這篇文章寫得還能夠,還請點個贊哦。

若是你以爲 nice-hooks 還不錯,還請加顆☆哦。

ByeBye!


Keywords: react, hooks, nice-hooks

相關文章
相關標籤/搜索