React应用程序重新渲染并使用axios无限获取数据

cor*_*ter 0 async-await reactjs firebase-hosting axios react-hooks

我是一名自学成才的开发人员,几乎完全使用原生 iOS 应用程序。对于我的一个项目,我聘请了一名 Web 开发人员来构建 React Web 应用程序(由 Firebase 托管)。他来自乌克兰,突然不得不停下来,所以我接手来完成它。因此,我无法以传统方式学习 React/HTTPS/axios/Node.js,即通过教程、练习、核心概念等缓慢学习。尽管几个月前,我还是能够完成它,一切都很好。然而,在过去的几周里,我不得不重构我的整个架构,包括创建一个新的 GCP 项目、一个新的 Firebase 项目、新的 git 存储库等,其中包括除了一些代码优化和数据模型之外的所有重新配置调整。正是在这次重组的某个时刻,我的问题出现了。我提到这一切是为了指出A)我一直严重依赖他的原创工作,尤其是在设置 Firebase Hosting 方面,B)我不太确定问题出在哪里。我 90% 确定它是用 React 实现的,但这很奇怪,因为从两个月前完成它开始,我并没有真正对异步网络调用进行任何更改,而且它按预期工作。

无论如何,在出现时,Web 应用程序请求向客户端呈现哪个视图,该视图是NotFoundView、 aReviewUserViewUserProfileView,由给定 a 的后端确定userId。问题是当UserProfileView显示时,某些原因会导致该视图无限地重新渲染。首先,在不超过两秒的时间内,它会正确显示此视图,然后非常快速地重置和重新渲染。useEffect我认为它与和/或React 钩子有关useState,尽管我不确定在哪个视图中。

非常感谢任何帮助,谢谢。

export enum ViewState { 
    UserProfile = 'UserProfile',
    ReviewForm = 'ReviewForm',
    NotFound = 'NotFound'
}

....................................................

export class ApiHandler { 
    // Returns a ViewState enum case for main view routing
    public static requestViewState(id: string): Promise<AxiosResponse<ViewState>> { 
        console.log('REQUESTING VIEW STATE')
        return axios.get(`${API_URL}/user/${id}/view-state`)
    }

    // Returns the requested user's profile data + other info
    public static requestUserData(id: string): Promise<AxiosResponse<UserData>> { 
        console.log('REQUESTING USER DATA')
        return axios.get(`${API_URL}/user/${id}`)
    }

    // More API calls ...
}

....................................................

export function RoutingView(props: RouteComponentProps<RoutingViewProps>) { 
    const userId = props.match.params.id
    const [viewState, setViewState] = useState<ViewState>()
    const [showError, setShowError] = useState<boolean>()
    const [showLoader, setShowLoader] = useState<boolean>(true)

    useEffect(() => { 
        loadViewState()
    }, [])

    if (showLoader) { 
        return <PageLoader />
    }

    if (showError) { 
        return <FailedToLoad />
    }

    switch (viewState) { 
        case ViewState.UserProfile:
            return <UserProfileView id={userId} />
        case ViewState.ReviewForm:
            return <ReviewUserView id={userId} />
        default:
            return <NotFoundView />
    }

    async function loadViewState(): Promise<void> { 
        setShowLoader(true)
        try { 
            const viewStateData = await ApiHandler.requestViewState(userId)
            setViewState(viewStateData.data)
        } catch (error) { 
            console.log(error)
            setShowError(true)
        }
        setShowLoader(false)
    }
}

....................................................

export default function UserProfileView(props: UserProfileProps) { 
    console.log('INITIALIZED USERPROFILEVIEW')
    const userId = props.id
    const [userData, setUserData] = useState<UserData>()
    const [showLoader, setShowLoader] = useState<boolean>(false)
    const [service, setService] = useState<Service | null>()
    const [loadFailed, setLoadFailed] = useState<boolean>()

    useEffect(() => {
        if (userData?.user) { 
            const user = userData?.user
            document.title = `${user?.firstName} ${user?.lastName}`
        }
    }, [userData])

    useEffect(() => { 
        loadUserData()
    }, [userData])

    if (loadFailed) { 
        return <FailedToLoad />
    }

    return <div>
        {showLoader ? <PageLoader/> : ''}

        { 
            userData?.user && <div className={service ? styles.Hidden : ''}>
                <UserInfo 
                    user={userData?.user}
                    services={userData?.services}
                    selectedService={(service) => setService(service)}
                    submitted={userData?.hasSubmitted}
                />
            </div>
        }

    // More view components ...

    </div>

    async function loadUserData(): Promise<void> { 
        setShowLoader(true)
        try {
            const res = await ApiHandler.requestUserData(userId)
            setUserData(res.data as UserData)
        } catch (error) { 
            console.log(error)
            setLoadFailed(true)
        }
        setShowLoader(false)
    }
}
Run Code Online (Sandbox Code Playgroud)

在本地调试时,这是控制台快速打印出来的内容。重复的模式可能表明了某些事情,尽管我不知道是什么。 注意:为了简单起见,我在这篇文章中重命名了一些内容,ReferralRequest.view实际上是UserProfileView.

在此输入图像描述

小智 6

相反,这样做:

//After every rendering ONLY IF `prop` or `state` changes in this case `userDate always change`
useEffect(() => { 
 loadUserData()
}, [userData])
Run Code Online (Sandbox Code Playgroud)

做这个:

//Runs ONCE after initial rendering
useEffect(() => { 
 loadUserData()
}, [])
Run Code Online (Sandbox Code Playgroud)

要说为什么会这样,你应该知道 useEffect(callback, dependency) 的依赖参数。

这里是使用情况的区别

1-)未提供依赖项:

每次渲染后都会产生副作用。

例子:

 useEffect(() => {
    // Runs after EVERY rendering
  });  
Run Code Online (Sandbox Code Playgroud)

2-)空数组[]:这是你应该做的。

副作用在初始渲染后运行一次。

例子:

 useEffect(() => {
    // Runs ONCE after initial rendering
  }, []);
Run Code Online (Sandbox Code Playgroud)

3-)有 props 或 state 值:这是你的问题。

仅当任何依赖值发生变化时,副作用才会发生。

例子:

useEffect(() => {
    // Runs ONCE after initial rendering
    // and after every rendering ONLY IF `prop` or `state` changes
  }, [prop, state]);
Run Code Online (Sandbox Code Playgroud)

  • 虽然这是事实,但请尝试解释为什么会出现这种情况,以及重新渲染和依赖项数组如何工作。 (2认同)
  • UserProfileView 重新渲染的原因是这样的,当你在其中调用 `loadUserData` 方法时,你会改变三次你的状态,第一次是当你用 setShowLoader(true) 改变 `showLoader` 时,第二次是当当您再次将“showLoader”更改为 false 时,您可以使用“setUserData(res.data as UserData)”设置收到的数据以及最后一个数据。但这一切都是正常的,你需要它。 (2认同)