为啥在 useEffect 中侦听套接字事件时对服务器有多个请求?

Posted

技术标签:

【中文标题】为啥在 useEffect 中侦听套接字事件时对服务器有多个请求?【英文标题】:Why I having multiple requests to the server when listening to socket event inside useEffect?为什么在 useEffect 中侦听套接字事件时对服务器有多个请求? 【发布时间】:2021-07-16 00:34:13 【问题描述】:

我收到了多个请求,尽管节点 js 服务器内只发出了一个事件。我只想在服务器发出事件时重新渲染我的组件,以便通知用户已成功完成。 这是我从服务器获取数据的自定义钩子:

import  useCallback, useState  from 'react';

export const useFetch = () => 
    const [loading, setLoading] = useState(false)
    const [error, setError] = useState(null)

    const request = useCallback(async (url, method='GET', body = null, headers = ) => 
        setLoading(true)
        try 
            if(body) 
                body = JSON.stringify(body)
                headers['Content-Type'] = 'application/json'
            

            const response = await fetch(url, method, body, headers)
            const data = await response.json()

            if(!response.ok) 
                throw new Error(data.msg || 'Что-то пошло не так!')
            

            setLoading(false)

            return data
         catch (e) 
            setLoading(false)
            setError(e.message)
            throw e            
        
    , [])

    const clearError = () => setError(null)

    return [loading, error, request, clearError]

这是我的 home.js 文件代码,我正在从服务器获取数据:

const Home = () => 
    const [loading, error, request, clearError] = useFetch()
    const [tables, setTables] = useState(null)

    useEffect(() =>     
        const handleFetchTables = async() => 
            try 
                const data = await request('/api/table/getAllTablesForCash')
                setTables(data)
             catch (_) 
                console.log(_.message)
                clearError()
            
        

        handleFetchTables()

        socketIO.on('new_order_finished', async() => handleFetchTables())
        socketIO.on('order_closed', async() => handleFetchTables())

    , [request])


    const handleTables = () => 
        if(loading) return <CustomSpinner />
        if(error) return <NetworkError />
        return tables && (
            tables.length ? (
                <div style=styles.main>
                    
                        tables.reverse().map((t, i) => 
                            return (
                                <TableCard key=i t=t />
                            )
                        )
                    
                </div>
            ) : (
                <EmptyBag />
            )
        )
    

    return (
        <div style=styles.container>
            <HomeHeader />
            handleTables()
        </div>
    )

.png

【问题讨论】:

请帮忙!它对性能有很大影响。特别是当我有更多的订单时,它会获取大约 21-22 次​​span> handleFetchTables 是否有可能以某种方式触发new_order_finishedorder_closed?另外,为什么requestuseEffect 依赖项中,而它的身份没有改变(由于useCallback)? 放几个console.log,应该会更清楚。 handleFetchTables 在两种情况下运行:1)作为效果运行 2)在套接字事件上运行。该效果在安装时运行,然后一旦request 的身份发生更改。 request 的身份没有改变(它是由useCallback 返回的,我没有看到它的使用有任何错误),所以我们剩下两种情况:1)组件Home 实际上正在重新安装(已安装 + 未安装)多次,无论出于何种原因,都会触发效果。 2) 套接字事件运行多次。 我建议您将其简化为一个最小的、可重现的示例。这将有助于发现问题。 另外,一旦Home 卸载,您就不会取消订阅套接字事件。因此,一旦您导航出Home,然后再次导航回Home,您将拥有越来越多的冗余侦听器,每次导航返回Home 时都会发送越来越多的请求。 Webpack 的开发服务器会在每次更改代码时重新加载,并且它也不会取消订阅事件。 【参考方案1】:

在您的 home.js 中,每次请求发生变化时都会调用 useEffect。 useEffect 块包含订阅套接字发出的事件的代码。因此,每次调用 useEffect 以更改“请求”时,您都在为该事件创建另一个订阅者。所以如果一个事件被触发,它会多次调用订阅的函数。

尝试从当前的 useEffect 中删除 [request] 部分。如果您真的想根据“请求”对象的更改做某事,请使用另一个 useEffect 块并且不要将事件订阅者放入其中。或者尝试在当前 useEffect 块的开头取消订阅,但这不是一个好的解决方案。希望这能解决您的问题

【讨论】:

为什么request 的身份会改变?据我所知,这是useCallback[] 作为部门的功能备忘录。

以上是关于为啥在 useEffect 中侦听套接字事件时对服务器有多个请求?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UseEffect 中将事件侦听器添加到 UseRefs

如何正确地将事件侦听器添加到 React useEffect 挂钩?

useEffect 中的套接字连接事件在创建组件时不起作用,但在 React 中刷新页面后起作用

为啥 SO_RCVTIMEO 从侦听套接字继承到接受的套接字? [关闭]

本地存储中的更改未触发事件侦听器

为啥这个 JS/jQuery 作用于多个输入?