在沒有導航屬性的情況下導航
有時候,您需要從無法存取 navigation
屬性的位置觸發導航動作,例如 Redux 中介軟體。對於這種情況,您可以使用 導航容器上的 ref
來傳送導航動作。
請勿在以下情況下使用 ref
- 您需要在無須傳遞
navigation
屬性的情況下從元件內部導航,請使用useNavigation
。ref
的行為方式不同,而且許多特定於畫面的輔助方法都不可用。 - 您需要處理深入連結或通用連結。使用
ref
執行此作業會有許多邊際案例。請參閱 設定連結 以取得更多有關處理深入連結的資訊。 - 您需要整合到第三方函式庫(例如推送通知、分支等)。請參閱 有關深入連結的第三方整合。
請於下列情況使用ref
- 當您使用狀態管理程式庫,例如 Redux,而您需要從中介軟體派送導航動作時。
請注意,通常由使用者動作 (例如按下按鈕) 觸發導航比由 Redux 中介軟體觸發來得更好。在使用者動作時進行導航會讓應用程式感覺反應更靈敏,並提供更好的使用者體驗。因此在使用ref
進行導航之前請先考慮這一點。ref
是為無法透過現有 API 處理的場景而設計的最後手段,僅應在少見的情況下使用。
用法
您可以透過ref
取得根導航物件,並將其傳遞給RootNavigation
,我們稍後將用於導航。
// App.js
import { NavigationContainer } from '@react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
在下一步中,我們定義了RootNavigation
,這個是一個包含派送使用者定義導航動作功能的簡單模組。
// RootNavigation.js
import { createNavigationContainerRef } from '@react-navigation/native';
export const navigationRef = createNavigationContainerRef();
export function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}
// add other navigation functions that you need and export them
然後,在任何一個 javascript 模組中,匯入RootNavigation
並呼叫您從中匯出的函式。您可以在 React 元件之外使用這種方式,而且實際上從 React 元件內部使用它時也能正常運作。
// any js module
import * as RootNavigation from './path/to/RootNavigation.js';
// ...
RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
除了navigate
之外,您還可以新增其他導航動作
import { StackActions } from '@react-navigation/native';
// ...
export function push(...args) {
if (navigationRef.isReady()) {
navigationRef.dispatch(StackActions.push(...args));
}
}
請注意,階層導航器需要被渲染才能處理此動作。您可能想要查看巢狀導航的文件以取得更多詳情。
在編寫測試時,您可以模擬導航功能,並判斷正確的函式是否以正確的參數被呼叫。
處理初始化
在使用這種模式時,您需要記住幾件事,以避免應用程式中的導航失敗。
ref
僅在導航容器渲染之後才會被設定,這在處理深度連結時可能會是非同步的- 導航器需要被渲染才能處理動作,沒有導航器,
ref
就無法準備就緒
如果您嘗試在未渲染導航器或導航器完成掛載之前進行導航,它將列印錯誤訊息,並且不會執行任何動作。因此,您需要新增額外的檢查,以決定應用程式掛載前該執行什麼動作。
例如,考慮以下情況,您在應用程式中的某個畫面中有一個畫面,且該畫面在useEffect
/componentDidMount
時會派送一個 redux 動作。您在中介軟體中偵聽此動作,並嘗試在收到時執行導航。這將會擲回錯誤,因為此時,父導航器尚未完成掛載且尚未準備就緒。父導航器的useEffect
/componentDidMount
總是在子導航器的useEffect
/componentDidMount
之後才會被呼叫。
為避免此問題,你可以使用引用上顯示的範例中 isReady()
方法。
// RootNavigation.js
import * as React from 'react';
export const navigationRef = createNavigationContainerRef();
export function navigate(name, params) {
if (navigationRef.isReady()) {
// Perform navigation if the react navigation is ready to handle actions
navigationRef.navigate(name, params);
} else {
// You can decide what to do if react navigation is not ready
// You can ignore this, or add these actions to a queue you can call later
}
}
如果你不確定瀏覽器是否已呈現,你可以呼叫 navigationRef.current.getRootState()
,如果你有呈現任何瀏覽器它會傳回有效的狀態物件,否則它會傳回 未定義
。