有時組件中的數據需要與外部系統的數據或操作同步,React提供了Hook Effect。
Effect 會在組件渲染后運行一些代碼,以便將組件與 React 之外的某些系統同步,包比如瀏覽器 API、第三方小部件,以及網絡請求等。
如以下的video播放器的簡單加載:
// 聲明 Effect
import { useEffect, useRef } from 'react';
/*
* src:來自父組件的props,視頻資源的地址
* isPlaying:來自父組件的props,視頻是否播放,boolean值
* */
function VideoPlayer({ src, isPlaying }) {
const ref = useRef(null);
/*
Effect 依賴 isPlaying prop 控制邏輯
如果 isPlaying 在上一次渲染時與當前相同,跳過運行 Effect
如果不添加依賴,組件每次渲染后都會執行 Effect
如果依賴為[],組件只在掛載后執行 Effect
*/
useEffect(() => {
// 組件渲染之后執行以下代碼
// 否則video dom尚未渲染,不存在play() 和 pause()方法
if (isPlaying) {
ref.current.play();
} else {
ref.current.pause();
}
}, [isPlaying]);
// 同步到 React state 的“外部系統”是瀏覽器媒體 API
return <video ref={ref} src={src} loop playsInline />;
}
export default function App() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<>
<button onClick={() => setIsPlaying(!isPlaying)}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<VideoPlayer
isPlaying={isPlaying}
src="water.mp4"
/>
</>
);
}
有時 Effect 需要指定如何停止、撤銷,或者清除它的效果。例如,“連接”操作需要“斷連”,“獲取”既需要“取消”也需要“忽略”。這時候就需要使用清理函數。
每次重新執行 Effect 之前,React 都會調用清理函數;組件被卸載時,也會調用清理函數。
嚴格模式時,在開發環境下,Effect出現額外的執行,是 React 正在調試你的代碼。
通常的解決辦法是實現清理函數,停止或撤銷 Effect 正在執行的任何操作。
在生產環境下,Effect只會執行一次。
如下面的模擬聊天室功能:
App.js
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
export default function ChatRoom() {
useEffect(() => {
//建立連接
const connection = createConnection();
connection.connect();
//清理函數,斷開連接
return () => connection.disconnect();
}, []);
return <h1>歡迎來到聊天室!</h1>;
}
chat.js
export function createConnection() {
// 連接的示例
return {
connect() {
console.log('連接中……');
},
disconnect() {
console.log('連接斷開。');
}
};
}
Effect應用舉例:
1、訂閱事件
如果 Effect 訂閱了某些事件,清理函數應該退訂這些事件:
useEffect(() => {
function handleScroll(e) {
console.log(window.scrollX, window.scrollY);
}
//頁面滾動事件
window.addEventListener('scroll', handleScroll);
//清理頁面滾動事件
return () => window.removeEventListener('scroll', handleScroll);
}, []);
2、觸發動畫
如果 Effect 對某些內容加入了動畫,清理函數應將動畫重置:
useEffect(() => {
const node = ref.current;
node.style.opacity = 1; // 觸發動畫
return () => {
node.style.opacity = 0; // 重置為初始值
};
}, []);
3、獲取數據
如果 Effect 將會獲取數據,清理函數應該要么終止數據獲取操作,要么忽略其結果:
useEffect(() => {
let ignore = false;
async function startFetching() {
const json = await fetchTodos(userId);
//不忽略結果,更新數據
if (!ignore) {
setTodos(json);
}
}
startFetching();
return () => {
ignore = true;
};
}, [userId]);
還有一些情形看似需要使用 Effect 實則并不需要,比如:初始化應用時,某些邏輯只需要在應用程序啟動時運行一次。比如,驗證登陸狀態和加載本地程序數據,可以將其放在組件之外:
if (typeof window !== 'undefined') { // 檢查是否在瀏覽器中運行
checkAuthToken();
loadDataFromLocalStorage();
}
function App() {
// ……
}
浙公網安備 33010602011771號