跳到主要內容
版本:6.x

useFocusEffect

有時候,我們會希望在畫面獲得焦點時執行動作。這個動作可能包含新增事件聆聽器、擷取資料、更新文件標題等。雖然可以使用 focusblur 事件就能達成,但這樣做不太符合人體工學。

為了讓這件事變簡單,此函式庫匯出了 useFocusEffect 掛勾

import { useFocusEffect } from '@react-navigation/native';

function Profile({ userId }) {
const [user, setUser] = React.useState(null);

useFocusEffect(
React.useCallback(() => {
const unsubscribe = API.subscribe(userId, (user) => setUser(user));

return () => unsubscribe();
}, [userId])
);

return <ProfileContent user={user} />;
}
警告

為避免動作執行太過頻繁,很重要的一點要在將回呼函式傳遞給 useFocusEffect 之前先將其包在 useCallback 裡,如同範例所示。

useFocusEffect 類似於 React 的 useEffect 掛勾。唯一不同的是只有畫面目前獲得焦點時才會執行。

當傳遞至 React.useCallback 的相依項變更時,這個動作便會執行,亦即會在初始呈現 (如果畫面獲得焦點) 時執行,以及後續呈現時 (如果相依項有變更)。如果您未將動作包在 React.useCallback 裡,則畫面獲得焦點時就會執行這個動作。

清理函式在需要清理上一個效果時執行,例如當相依項變更且安排新效果時,以及當畫面卸載或模糊時。

執行非同步效果

當執行非同步效果(例如從伺服器擷取資料)時,務必確認你在清理函式中取消要求(類似於 React.useEffect)。如果你使用沒有提供取消機制的 API,請務必略過狀態更新

useFocusEffect(
React.useCallback(() => {
let isActive = true;

const fetchUser = async () => {
try {
const user = await API.fetch({ userId });

if (isActive) {
setUser(user);
}
} catch (e) {
// Handle error
}
};

fetchUser();

return () => {
isActive = false;
};
}, [userId])
);

如果你沒有略過結果,你可能會因為 API 呼叫中的競爭狀態而得到不一致的資料。

推遲效果直到轉場結束

useFocusEffect 掛勾會在畫面切換為焦點後立即執行效果。這通常意謂著如果畫面變更中有動畫,它可能尚未結束。

React 導覽在原生執行緒中執行其動畫,因此在許多案例中這不是問題。但如果效果更新 UI 或呈現某個昂貴的東西,那麼它會影響動畫效能。在這種情況下,我們可以使用 InteractionManager 來延後我們的作業,直到動畫或手勢結束

useFocusEffect(
React.useCallback(() => {
const task = InteractionManager.runAfterInteractions(() => {
// Expensive task
});

return () => task.cancel();
}, [])
);

useFocusEffect 與新增 focus 事件的監聽方式有何不同

當畫面切換為焦點時,focus 事件會觸發。因為它是事件,如果你在訂閱事件時畫面已經具有焦點,你的監聽不會被呼叫。這也無法提供一種在畫面失去焦點時執行清理函式的方法。你可以訂閱 blur 事件並手動處理,但可能會很混亂。通常你也需要處理 componentDidMountcomponentWillUnmount,此外還要處理這些事件,這讓它更複雜。

useFocusEffect 讓你能夠在焦點時執行一個效果,然後在畫面失去焦點時清理它。它也會在卸載時處理清理。它會在相依項變更時重新執行效果,因此你不用擔心監聽器中的值過時。

在什麼時候改用 focusblur 事件

useEffect,一個清理函式可以從 useFocusEffect 中的效果傳回。清理函式的目的是清理效果 - 例如中止非同步工作、取消訂閱事件監聽等等。它不是用來在 blur 中做某件事的。

例如,請勿執行以下操作

useFocusEffect(
React.useCallback(() => {
return () => {
// Do something that should run on blur
};
}, [])
);

清除功能會在效果需要清除時執行,例如在「模糊」、卸載或變更依賴項時執行。這並不是更新狀態或執行應在「模糊」時發生的動作的好地方。您應該改為傾聽「模糊」事件

React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
// Do something when the screen blurs
});

return unsubscribe;
}, [navigation]);

此外,如果您想要在畫面獲得焦點時執行動作(例如追蹤畫面焦點),而不需要清除或不需要在依賴項變更時重新執行,則應改為使用「焦點」事件

與類別元件一起使用

您可以為您的效果製作一個元件,並在您的類別元件中使用它

function FetchUserData({ userId, onUpdate }) {
useFocusEffect(
React.useCallback(() => {
const unsubscribe = API.subscribe(userId, onUpdate);

return () => unsubscribe();
}, [userId, onUpdate])
);

return null;
}

// ...

class Profile extends React.Component {
_handleUpdate = (user) => {
// Do something with user object
};

render() {
return (
<>
<FetchUserData
userId={this.props.userId}
onUpdate={this._handleUpdate}
/>
{/* rest of your code */}
</>
);
}
}