更正了依赖数组,以便使用 React 钩子使用效果



我正在使用Create-React-App和(优秀的(use-http进行自定义useFetch钩子。目标是在登录到帐户区域时进行多次 API 调用:

const [user, setUser] = useState(null)
const [profile, setProfile] = useState(null)
const [posts, setPosts] = useState(null)
const request = useFetch('/')
const initializeAccount = async () => {
try {
const user = await request.get('api/user/')
const profile = await request.get('api/profile/')
const posts = await request.get('api/posts/')
if (user) {
setUser(user.data)
}
if (profile) {
setProfile(profile.data)
}
if (posts) {
setPosts(posts.data)
}
} catch (e) {
console.log('could not initialize account')
}
}
useEffect(() => {
initializeAccount()
return () => console.log('unmount')
})

我尝试使用[]作为依赖数组,但我收到一个 linting 错误,说initializeAccount移动到依赖数组。如果我添加它,该函数将无休止地运行。

设置依赖项数组以便调用此函数一次的正确方法是什么?此外,在这种情况下,处理每个 API 调用中止的正确方法是什么?

我的伙计,为了对 api 调用运行一次 useEffect,你必须这样做:

useEffect(() => {
initializeAccount()
return () => console.log('unmount')
},[])

希望对您有所帮助。

lint 错误是有原因的,忽略它可能会导致很难找到的错误。

无依赖关系数组

如果完全排除依赖数组(useEffect(() => { /* ... */ });(,效果将在每次渲染上运行,导致无限循环。

空依赖项数组

如果添加一个空的依赖数组 (useEffect(() => { /* ... */ }, [])(,效果将仅在第一次渲染时运行。这可能在这里有效,但它会导致其他情况下的问题,或者将来发生某些变化。例如,假设request初始化需要一些时间(所以它是第一个undefined,稍后更改为对象(,initializeAccount如下所示。现在initalizeAccount只会在第一次使用request === undefined渲染时运行,当request在稍后阶段准备就绪并且组件被重新渲染时,initalizeAccount不会再次运行,并且功能将静默失败。

const initializeAccount = async () => {
if (request) {
const user = await request.get('api/user/')
if (user) {
setUser(user.data)
}
}
}

填充的依赖数组(正确的方式(

如果将initializeAccount添加到依赖数组 (useEffect(() => { /*...*/ }, [initalizeAccount])(,则每次更改initializeAccount时都会运行效果。对于对象(和函数(,React 将上次渲染的引用与当前渲染的引用进行比较,以确定对象是否已更改。您定义initializeAccount的方式将在每次渲染时重新定义,因此引用将是新的,并且效果将在每次渲染上运行,就像您体验的那样。

您需要做的是获得对initalizeAccount的稳定引用。为此 React 提供了useCallback,它类似于useEffect,因为它有一个依赖数组。如果我们用useCallback定义initializeAccount,React 将确保渲染之间的引用相同。只有当useCallback的依赖数组中的一个依赖项发生变化时,才会重新定义函数,进而触发useEffect再次运行。这样,您可以遵守lint规则,同时效果仅运行一次。

const initializeAccount = useCallback(async () => {
if (request) {
const user = await request.get('api/user/')
if (user) {
setUser(user.data)
}
}
}, [request])

请注意,如果request反过来没有稳定的引用,useCallback将在每个渲染上运行(如上所述(,然后useEffect也是如此。如果useFetch是由您编写的,请确保它返回具有稳定引用的对象。如果useFetch是从库中提供的,则很可能已经如此。

TL;博士

initializeAccount包装在useCallback中,并将其添加到useEffect的依赖项数组中。

const initializeAccount = useCallback(async () => {
try {
const user = await request.get('api/user/')
const profile = await request.get('api/profile/')
const posts = await request.get('api/posts/')
if (user) {
setUser(user.data)
}
if (profile) {
setProfile(profile.data)
}
if (posts) {
setPosts(posts.data)
}
} catch (e) {
console.log('could not initialize account')
}
}, [request])
useEffect(() => {
initializeAccount()
return () => console.log('unmount')
}, [initializeAccount])

相关内容

  • 没有找到相关文章

最新更新