09-09-2021
使用時在component中需把useEffect
從react
函式庫中引入
import { useEffect } from 'react';
若需要與其他的變數聯合起來需要需要寫成:
import React, { useState, useEffect } from 'react';
我們的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)將顯示的變數寫在裡面
若沒有將effect
return clean up ,那每次document 在component 重新渲染時都會將一個新的事件監聽氣添加到DOM中,不僅會導致錯誤,也會讓效能下降
effect()
都是在渲染之後執行,而且不是一次,是每次React 重新渲染時都會執行。所以我們在渲染之前和卸載之後使用clean up effect
清理每一個調用的效果若我們的effect retrun是一個function,那use effect() 就會把他當做是一個 cleanup function。 React 會在重新渲染之前或是卸載之後呼叫cleanup function 。
memory leak
所以要清除掉* memory leak
(記憶體管理)
當value在被宣告時同時也完成了記憶體的配置,且會自動釋放不再使用的值。
回收的機制主要是「參考概念」,如果物件中會使用到另外一個物件,即是該物件參考另外一個物件。
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);
};
});
在定義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的值發生變化時再次調用
當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>
錯誤示範:
if (userName !== '') {
useEffect(() => {
localStorage.setItem('savedUserName', userName);
});
}
正確示範
useEffect(() => {
if (userName !== '') {
localStorage.setItem('savedUserName', userName);
}
});
將數據分門管理取代只有一個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>
)}