React Native - 将回调传递到另一个屏幕

问题描述 投票:0回答:2

我有一个带有表格的屏幕。现在,在该表单中,有一个按钮可以打开另一个屏幕,用户可以在其中选择目标选项,选择后,他们将导航回原始表单(堆栈导航器),原因是用户还可以在那里创建一个新选项,以便他们转到与单独编辑选项时相同的 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

有没有更好的方法如何将回调发送到另一个屏幕?

javascript typescript react-native react-native-navigation
2个回答
5
投票

不建议通过

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])

0
投票

这是我使用 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 获取数据。所以传递一个回调函数对我来说更直接。

© www.soinside.com 2019 - 2024. All rights reserved.