【问题标题】:UseEffect execution somehow depending on function call within itselfUseEffect 执行以某种方式取决于其自身的函数调用
【发布时间】:2020-06-06 16:05:16
【问题描述】:

我在 React Native 中使用 useEffect-hook 时遇到了一个特殊的问题。我有一个功能组件,它有一个 useEffect-hook 用于获取精确定位数据,另一个用于将精确定位 (filteredPinpoints) 重新排列为可用格式。 filteredPinpoints 更新了 3 次,但是前两次,对象是空的。

现在奇怪的行为:如果我在第二个 useEffect 中注释掉 dispatch(organiseRoutes(...)),这个 useEffect 会被调用 3 次,但是如果我想执行 dispatch 函数,useEffect 只会被调用两次。如果filteredPinpoints 为空,我会提前返回,因此代码永远不会到达调度程序。

编辑:另外,当我实现dispatch(organiseRoutes(...)) 时,应用程序冻结,只显示(旋转的)ActivityIndi​​cator,但让我无法再次导航到上一个屏幕。

我必须更改什么,以便每次更新 filteredPinpoints 时都会运行 useEffect?

import { View, ActivityIndicator } from 'react-native';
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getRouteData, organiseRoutes } from '../utils';

export default function RoutePreviewScreen() {

    const dispatch = useDispatch();
    const [loadingData, setLoadingData] = useState(true);
    const currentRouteID = useSelector(state => state.currentRouteID);
    const filteredPinpoints = useSelector(state =>
        // Uses ObjectFilter from https://stackoverflow.com/questions/5072136/javascript-filter-for-objects/37616104
        ObjectFilter(state.allPinpoints, pinpoint => pinpoint.Route_ID == state.currentRouteID)
    );

    const dispatch = useDispatch();

    // This updates state.allPinpoints.
    useEffect(() => {
        (async function myFirstAsyncFunction() {
            await dispatch(getRouteData(currentRouteID));
        })();
    }, [currentRouteID]);

    useEffect(() => {
        if (Object.keys(filteredPinpoints).length === 0) {
            return
        }

        console.log("Could EXECUTE now!!")

        // If the following line is commented out, the useEffect executes a third time.
        // However, only in the third run, filteredPinpoints is not a empty object.
        // If it is not commented out, it simply refuses to execute a third time.
        dispatch(organiseRoutes(filteredPinpoints));

        setLoadingData(false)
    }, [filteredPinpoints]);

    if (loadingData) { return (<View><ActivityIndicator/></View>)}

    return(<ComponentUsingOrganisedRoutes/>)

【问题讨论】:

    标签: javascript reactjs react-native react-hooks


    【解决方案1】:

    看起来你的filteredPinpoints 是一个对象。 useEffect 不对对象进行深度相等检查,因为您必须以不同的方式处理它。

    您可以使用类似的依赖项:[JSON.stringify(filteredPinpoints)],这样可以进行更好的检查,并且不会像深度相等检查那样慢。

    参考:https://twitter.com/dan_abramov/status/1104414272753487872

    【讨论】:

    • 刚刚检查过,但行为是一样的。 console.log("Could EXECUTE now!!") 仅在 dispatch(organiseRoutes(filteredPinpoints)) 被注释掉时才被调用。
    • 也许尝试添加 Object.keys(filteredPinpoints).length 作为 useEffect 依赖项?
    【解决方案2】:

    好的,解决了。相当愚蠢的错误。基本上,organiseRoutes() 会影响filteredPinpoints,所以useEffect 会陷入无限循环。我猜在这些情况下,console.log() 不会被调用,因为它被阻止了。

    我在loadingData 旁边引入了一个新的状态钩子organisedData,为第二个useEffect 提供另一个值,以便在它执行调度之前检查它。一旦organisedData 变为true,它将停止执行,从而停止触发自身。

    import React, { useState, useEffect } from 'react';
    import { View, ActivityIndicator } from 'react-native';
    import { useDispatch, useSelector } from 'react-redux';
    import { getRouteData, organiseRoutes } from '../utils';
    
    export default function RoutePreviewScreen() {
    
        const dispatch = useDispatch();
        const [loadingData, setLoadingData] = useState(true);
        const currentRouteID = useSelector(state => state.currentRouteID);
        const filteredPinpoints = useSelector(state =>
            ObjectFilter(state.allPinpoints, pinpoint => pinpoint.Route_ID == state.currentRouteID)
        );
    
        // New:
        const [organisedData, setOrganisedData] = useState(false);
    
        useEffect(() => {
            (async function myFirstAsyncFunction() {
    
                // New:
                setLoadingData(true)
    
                await dispatch(getRouteData(currentRouteID));
    
                // New:
                setOrganisedData(false)
            })();
        }, [currentRouteID]);
    
        useEffect(() => {
    
            // New:
            if (!loadingData || organisedData) {
                return
            }
    
            if (Object.keys(filteredPinpoints).length === 0) {
                return
            }
    
            // New:
            setOrganisedData(true)
    
            dispatch(organiseRoutes(filteredPinpoints));
    
            // New:
            setLoadingData(false)
    
        // Adjusted:
        }, [organisedData, loadingData, filteredPinpoints]);
    
        if (loadingData) { return (<View><ActivityIndicator/></View>)}
    
        return(<ComponentUsingOrganisedRoutes/>)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多