iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Modern Web

<法式Terrine : React next.js Chakra-UI styled-component 精緻的搭配>系列 第 9

< 關於 React: 開始打地基| useEffect() >

  • 分享至 

  • xImage
  •  

09-09-2021

本章內容
  • function component effects
    • 創立一個彈跳視窗
  • Clean Up Effects
  • 當effect 被呼叫時的控制方式
  • Fetch Data時的用法
  • Hooks 的規則
  • 獨立使用每一個Hook與Effects
  • useEffect 回顧

Function Component Effects

使用時在component中需把useEffectreact函式庫中引入

import { useEffect } from 'react';

若需要與其他的變數聯合起來需要需要寫成:

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

使用useEffect()創立一個彈跳視窗

我們的effect在function component渲染後被調用,但仍然可以使用function component中的變數。
當React 渲染function component後,會一樣的更新DOM,然後在DOM更新後運行我們的效果。

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

export default function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    alert(`Count: ${count}`);
  });

  const handleClick = () => {
    setCount((prevCount) =>  prevCount + 1);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>
        Click me
      </button>
    </div>
  );
}

一定要在調用State Hook之後使用Effect Hook,這樣我們才能使用到count變數和之前的handleClick()函數。

useEffect(() => {
    alert(`Count: ${count}`);
  });

使用useEffect()調用
alert()使用(Template literals)反引號(back-tick)將顯示的變數寫在裡面

Clean Up Effects

若沒有將effect return clean up ,那每次document 在component 重新渲染時都會將一個新的事件監聽氣添加到DOM中,不僅會導致錯誤,也會讓效能下降

  • 解決方式:因為effect()都是在渲染之後執行,而且不是一次,是每次React 重新渲染時都會執行。所以我們在渲染之前和卸載之後使用clean up effect清理每一個調用的效果

若我們的effect retrun是一個function,那use effect() 就會把他當做是一個 cleanup function。 React 會在重新渲染之前或是卸載之後呼叫cleanup function 。

  • cleanup function 是非必需的,但我們避免產生memory leak 所以要清除掉

* memory leak
(記憶體管理)
當value在被宣告時同時也完成了記憶體的配置,且會自動釋放不再使用的值。

生命週期:
  1. 配置程式需要的記憶體
  2. 使用配置到的記憶體空間(讀、寫)
  3. 不自使用時釋放已經被配置的記憶體空間

回收的機制主要是「參考概念」,如果物件中會使用到另外一個物件,即是該物件參考另外一個物件。
JavaScript 中的proptotype以及該物件的屬性,即是隱式參考(前者)以及顯示參考(後者)

Reference-counting garbage collection
可以用「沒有其他物件參考它」簡言之,如果一個物件沒有被其他物件參考,即可被視為可以被回收的記憶體垃圾。

// 範例
import React, { useState, useEffect } from 'react';

export default function Counter() {
  const [clickCount, setClickCount] = useState(0);

  const increment = () => setClickCount((prev) => prev + 1);

  useEffect(() => {
    document.addEventListener('mousedown', increment);
    return () => {
      document.removeEventListener('mousedown', increment);
    };
  });

  return (
      <h1>Document Clicks: {clickCount}</h1>
  );
}

監聽滑鼠按下的監聽事件,cleanup function 是一個新的function內容在effect中return

useEffect(() => {
    document.addEventListener('mousedown', increment);
    return () => {
      document.removeEventListener('mousedown', increment);
    };
  });

當effect 被呼叫時的控制方式

在定義function component時,使用effect 通常只有在component在mounts時候(renders 的第一次),而不是在re-render的時候,而Effect Hook 可以讓我們達成這件事情。

如果我們想要在第一次渲染後調用effect,我們傳遞一個空的array給useEffect()作為第二個參數。
而這個第二個參數稱為==依賴陣列(dependency array)==
依賴陣列告訴useEffect()何時該調用effect何時該跳過它。

effect 總是會在第一次渲染後調用 ; 但只有在依賴陣列中的某些內容渲染間更改了value才會再次調用

// 範例
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 只會在count的值發生變化時再次調用
  • 依賴陣列、第一次渲染後調用的效果:
    不明確時:每次重新渲染
    空陣列時:不重新渲染
    非空陣列時:當依賴的陣列中的任何值發生變化

Fetch Data時的用法

當component 渲染的數據沒有變化時,我們可以傳遞一個空的陣列(依賴陣列),在第一次渲染後獲得數據,當收到來自伺服器的響應時,我們可以使用 State Hook 中的setState()將來自伺服器響應的數據儲存在我們本地組件狀態中以供渲染。這種方式一起使用 State Hook 和 Effect Hook,可以避免我們的component在每次渲染後不必要地獲取新數據!

一個非空的依賴陣列向 Effect Hook 發出請求時,它可以在重新渲染後跳過調用我們的效果,除非我們的依賴陣列中的一個變數的value發生了變化。如果依賴項的值發生了變化,那麼 Effect Hook 會再次調用我們的 effect!

import React, { useState, useEffect } from 'react';
import { get } from './Backend/fetch';

export default function Forecast() {
  const [data, setData] = useState(null);
  const [file, setFile] = useState({});
  const [fileList, setFileList] = useState('/korea');

  useEffect(() => {
    alert('資料來了···');
    get(fileList).then((response) => {
      alert('Response: ' + JSON.stringify(response,'',2));
      setData(response.data);
    });
  }, [fileList]);

  const handleChange = (itemId) => ({ target }) =>
    setFile((prev) => ({
      ...prev,
      [itemId]: target.value
    }));

  if (!data) {
    return <p>Loading...</p>;
  }

  return (
    <div className='App'>
      <h1>影片區</h1>
      <div>
        <button onClick={() => setFileList('/korea')}>韓劇館</button>
        <button onClick={() => setFileList('/japan')}>日劇館</button>
      </div>
    </div>
  );
}

使用 if 在沒有接到數據時渲染出Loading..


if (!data) {
    return <p>Loading...</p>;
}

將接到的數據存在setData()之中
使用useEffect的第二個參數,一個空的陣列,確保我們的component 在第一次渲染之後才拿到數據

  useEffect(() => {
    alert('資料來了···');
    get(fileList).then((response) => {
      alert('Response: ' + JSON.stringify(response,'',2));
      setData(response.data);
    });
  }, [fileList]);

將fetch 的api 整理成獨立一份文件,使用get變數引入

在方法中使用變數forecastType的值來確定要去調用哪個端點的資料,如此只要值有改變就會決定要去調用哪一個端點/korea \ /japan

import { get } from './Backend/fetch';

  useEffect(() => {
    alert('資料來了···');
    get(fileList).then((response) => {
      alert('Response: ' + JSON.stringify(response,'',2));
      setData(response.data);
    });
  }, [fileList]);

//
return 
button onClick={() => setFileList('/korea')}>韓劇館</button>

Hooks 的規則

  1. 只能在top level調用Hooks
  2. 只從React 函數中調用Hooks
  3. 只在React function 中使用,不能在class function中使用
  4. 不能在一般JavaScript函數中使用
  5. 自定義的Hook中使用

錯誤示範:

if (userName !== '') {
  useEffect(() => {
    localStorage.setItem('savedUserName', userName);
  });
}

正確示範


useEffect(() => {
  if (userName !== '') {
    localStorage.setItem('savedUserName', userName);
  }
});

獨立使用每一個Hook與Effects

將數據分門管理取代只有一個data下的使用方式,由於眾多資料都歸在同一個data上,不僅會造成閱讀上的障礙,也使得使用effect上充滿衝突,將data內使用到的項目分門別類的設置 useState()管理,再將從data中取得的資料儲存到setState以供後續提用。

import React, { useState, useEffect } from 'react';
import { get } from './mockBackend/fetch';

export default function SocialNetwork() {
  const [menu, setMenu] = useState(null);
  useEffect(() => {
    get('/menu').then((response) => {
      setMenu(response.data);
    });
  }, []);
// 更多其他引入的data省略..
  return (
    <div className='App'>
      <h1>My Network</h1>
      {!menu ? (
        <p>Loading..</p>
      ) : (
        <nav>
          {menu.map((menuItem) => (
            <button key={menuItem}>{menuItem}</button>
          ))}
        </nav>
      )}
    // 更多資料呈現省略..
    </div>
  );
}

在這裡使用了三元方程式判別接取到data的情境,以及使用menu做.map的方式將資料撈出顯示。

{!menu ? (
        <p>Loading..</p>
      ) : (
        <nav>
          {menu.map((menuItem) => (
            <button key={menuItem}>{menuItem}</button>
          ))}
        </nav>
      )}

useEffect 回顧

  1. 從react庫中導入這個函數並在function component中調用
  2. effect 指的是function當作參數傳遞的useEffect()函數,默認在每次渲染後調用此效果
  3. cleanup function 可選擇return的function,如果effect做了需要清理防止memory leek的事情,effect會返回一個cleanup ,那麼effect hook 就會再次調用effect之前以及在卸載component時調用cleanup function
  4. dependency array: 這是可選的第二個參數,可以使用useEffect()使用參數調用函式,防止在不需要時重複調用,陣列應該要包含effect 所依賴的變數。

上一篇
< 關於 React: 開始打地基| useState()>
下一篇
< 關於 React: 開始打地基| LifeCycle 生命圈>
系列文
<法式Terrine : React next.js Chakra-UI styled-component 精緻的搭配>18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言