【问题标题】:Storing and calling functions in React Context leads to weird behavior在 React Context 中存储和调用函数会导致奇怪的行为
【发布时间】:2021-06-17 12:55:03
【问题描述】:

我试图在 React 上下文中存储一个简单的回调,该回调由一个组件设置,然后由另一个组件调用,以促进它们之间的通信。这是因为这两个组件没有任何子父关系,并且位于应用程序的不同子树中。而且,我想将 Component1 中的自定义事件处理程序分配给 Component2 的点击行为。

因此,我使用函数(回调)及其设置器创建了上下文

import { createContext, useContext, useState } from "react"
interface ContextShape {
    myCallback?: (searchInput: string) => void
    setMyCallback: (onSearchCallback: (searchInput: string) => void) => void
}

const MyContext = createContext<ContextShape>({
    myCallback: () => {},
    setMyCallback: () => {},
})

export const useMyContext = () => useContext(MyContext)

export const MyContextProvider: React.FC = (props) => {
    const [myCallback, setMyCallback] = useState<(searchInput: string) => void>(() => {})

    return (
        <MyContext.Provider
            value={{
                myCallback: myCallback,
                setMyCallback: (clientCallback) => {
                    setMyCallback(clientCallback)
                },
            }}
        >
            {props.children}
        </MyContext.Provider>
    )
}

点击事件所在的组件,调用回调如下:

const { myCallback } = useMyContext()

const handleKeyPress = (
    e: React.KeyboardEvent<HTMLInputElement>,
    sVal: string | undefined
) => {
    if (e.code === "Enter" && myCallback) {
        myCallback(sVal || "")
    }
}

而且,我正在尝试从我的应用程序的页面中设置回调:

useEffect(() => {
    // eslint-disable-next-line no-console
    setMyCallback(() => (text: string) => console.log("Hii" + text))
}, [])

注意到一些奇怪的东西吗?在设置回调时,我必须将它包装在另一个函数中(() =&gt; {... my callback...})。只有这样,它才会按预期运行。如果我直接传递回调,那么它会立即被调用,而不是被存储和稍后调用。 (我广泛检查了它是否在某处被错误地调用,但代码在任何地方都没有无意中执行myCallback())。这对我来说是出乎意料的,因为我希望 (text: string) =&gt; {...} 足以进行回调,并不表示我想立即调用该函数。

【问题讨论】:

    标签: javascript reactjs react-hooks react-context


    【解决方案1】:

    你必须创建一个柯里化函数作为回调的原因是状态更新器也将一个回调函数作为它的参数,它会立即调用:

    例如:

    setMyCallback((prev) => {
       console.log(prev, 'prev state');
    });
    

    现在,如果您只是将回调函数传递给 setMyCallback 函数,它会被视为

    setMyCallback((text: string) => console.log("Hii" + text));
    

    因此console.log() 会立即被调用。

    您可以将代码更新为如下以避免此类问题:

    export const MyContextProvider: React.FC = (props) => {
        const [myCallback, setMyCallback] = useState<(searchInput: string) => void>(() => {})
    
        return (
            <MyContext.Provider
                value={{
                    myCallback: myCallback,
                    setMyCallback: (clientCallback) => {
                        // Already use a callback function
                        setMyCallback(() => clientCallback)
                    },
                }}
            >
                {props.children}
            </MyContext.Provider>
        )
    }
    

    然后把它当作

    useEffect(() => {
        // eslint-disable-next-line no-console
        setMyCallback((text: string) => console.log("Hii" + text))
    }, []);
    

    【讨论】:

    • 啊!因此,将函数传递给状态更新器使其认为我们正在使用setState 的第二种形式(带有回调),因此它会立即调用它——我猜对了吗?
    • 谢谢@Shubham。那是菜鸟的错误。拿上你的绿勾! :)
    猜你喜欢
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 2018-09-16
    • 2016-12-14
    • 2017-01-07
    • 2015-01-27
    相关资源
    最近更新 更多