我有一个带有表格的屏幕。现在,在该表单中,有一个按钮可以打开另一个屏幕,用户可以在其中选择目标选项,选择后,他们将导航回原始表单(堆栈导航器),原因是用户还可以在那里创建一个新选项,以便他们转到与单独编辑选项时相同的 UI(他们可以)。
我的解决方案下面的代码有效,但是我收到以下警告:
在导航状态中发现不可序列化的值。这个可以 中断使用,例如持久化和恢复状态。这可能会发生 如果您传递了不可序列化的值,例如函数、类 参数中的实例等。如果您需要使用带有回调的组件 在您的选项中,您可以使用“navigation.setOptions”代替。看 https://reactnavigation.org/docs/5.x/troubleshooting#i-get-the-warning-non-serialized-values-were-found-in-the-navigation-state 了解更多详情。
现在该链接建议使用
setOptions
,但这更像是在标题中添加按钮,但情况并非如此,用户通过单击选项在目标屏幕中选择该选项。
以下是代码的重要部分:
// MAIN FORM SCREEN
<Button title={"Some Title"} onPress={openSelectItem}></Button>
const openSelectFish = () => {
return navigation.navigate("ItemList", { sendItemBack: true, onGoBack: onSelectItem });
};
const onSelectItem = (item: ItemDTO) => {
setItem(item.name); // basic useState
};
// THE OPTIONS SCREEN
const onChooseItem = (item: ItemDTO) => {
console.log(this);
if ((typeof route !== "object") || (typeof route.params !== "object")) return;
if (!route.params.sendItemBack ?? true) return;
route.params.onGoBack(item);
return navigation.goBack();
};
// onChooseItem is then used in onPress of each option
有没有更好的方法如何将回调发送到另一个屏幕?
不建议通过
route params
传递回调函数。从你的代码和你的解释来看,你实际上想要pass data back to the screen on goBack
。
而不是做
route.params.onGoBack(item);
return navigation.goBack();
我们可以将
item
传递回 MainFormScreen
上的路由参数中的 navigation.goBack()
,并直接在 MainFormScreen
中的 useEffect
中设置状态。这使我们不必在路由参数中传递函数。相反,我们直接在表单中设置状态。
OptionsScreen 我们可以像往常一样使用
goBack
并将 navigate
传回 item
,而不是调用
MainFormScreen
return navigation.navigate('MainFormScreen', { item: item })
MainFormScreen 在这里,我们不再传递函数,而是对路由参数更改做出反应。
const openSelectFish = () => {
return navigation.navigate("ItemList", { sendItemBack: true });
};
const params = useMemo(() => route.params || {}, [route.params])
const [item, setItem] = useState()
const onSelectItem = useCallback((item: ItemDTO) => {
setItem(item.name); // basic useState
}, [setItem]);
useEffect(() => {
onSelectItem(params.item)
}, [params, onSelectItem])
这是我使用 React Navigation 在屏幕之间传递回调的解决方案:
NavigationHelper.ts
import { useNavigation } from "@react-navigation/native";
import React, { useContext, useEffect } from "react";
type AnyFunction = (...args: any[]) => any;
export interface NavigationHelper {
navigateWithCallback: (options: {
name: string;
key?: string;
params: any;
merge?: boolean;
callback: AnyFunction
}) => void;
}
const contextRef = React.createContext(new Map<string, AnyFunction>())
function randomStringId() {
return (new Date().getTime().toString() + Math.random().toString()).replace('.', '')
}
export function useNavigationHelper(): NavigationHelper {
const navigation = useNavigation() as any
const callbackMap = useContext(contextRef)
return {
navigateWithCallback(options) {
if (!options.callback) {
throw Error('options.callback can not be null')
}
const id = randomStringId()
callbackMap.set(id, options.callback)
navigation.navigate({
...options,
callback: undefined,
params: {
...options?.params,
callbackHandle: id
}
})
}
}
}
export function useNavigationCallback(callbackHandle:string): AnyFunction {
const callbackMap = useContext(contextRef)
const callback = callbackMap.get(callbackHandle)
if (!callback) {
throw Error('callback not found, check the callBackHandle:' + callbackHandle)
}
useEffect(() => {
return () => {
// clear callback object to avoid memory leak
callbackMap.delete(callbackHandle)
}
}, [])
return callback
}
假设我们有两个屏幕,一个 Webview 屏幕显示网页,一个 Search 屏幕用于输入 url。最初我停留在 Webview 屏幕,然后跳到搜索屏幕,然后输入 url,按 Enter 键,然后返回 Webview 屏幕并加载该 url。
这是伪代码:
// The original screen
const WebviewScreen() {
const navigationHelper = useNavigationHelper();
const webviewRef = useRef();
const goSearch = useCallback(() => {
// pass an callback function to next screen
navigationHelper.navigateWithCallback({
name: 'SearchScreen',
params: {
initUrl: "example.com",
},
callback: (url) => {
console.log('The url send back:', url)
// trigger an event
webview.current.loadUrl(url)
}
})
}, [])
return <WebView ref={webviewRef} onPress={goSearch} >
</WebView>
}
// The destination screen
const SearchScreen({navigation, route}) {
// retrive the `callbackHandle` param added by `NavigationHelper`
const { initUrl, callbackHandle } = route.params
// use custom hook
const naviCallback = useNavigationCallback(callbackHandle);
return <Button onPress={()=>{
// use the callback function passed from previous screen
naviCallback(initUrl)
navigation.goBack()
}}></Button>
}
这样
navigation.navigate({
name: 'SearchScreen',
params: {
initUrl: "example.com",
callback: (url) => {
console.log('The url send back:', url)
webview.current.loadUrl(url)
}
},
})
因为这样你会收到警告:
function Object is not serializable
我们可以使用
navigation.navigate
返回上一个屏幕。
// The original screen
const WebviewScreen({route, navigation}) {
const {url} = route.params
const webviewRef = useRef();
const goSearch = useCallback(() => {
navigation.navigate({
name: 'SearchScreen',
params: {
initUrl: 'example.com',
}
})
}, [])
return <WebView url={url} ref={webviewRef} onPress={goSearch} >
</WebView>
}
// The destination screen
const SearchScreen({navigation, route}) {
const { initUrl } = route.params
return <Button onPress={()=>{
navigation.navigate({
name: 'WebviewScreen',
params: {url:initUrl},
merge:true
})
}}></Button>
}
但就我而言,当从 SearchScreen 返回时,我需要触发一个事件,而不是仅仅从 SearchScreen 获取数据。所以传递一个回调函数对我来说更直接。