[英]How to use useEffect() to fetch data from an api in a useReducer() and useContext() state management setup?
reducer.js
export const reducer = (state, action) => {
switch(action.type) {
case SET_HEROES:
return { ...state, heroes: action.heroes }
}
}
AppContext.js
export const AppContext = React.createContext()
export const AppProvider = (props) => {
const initialState = {
heroes: []
}
const [appState, dispatch] = useReducer(reducer, initialState)
const setHeroes = async () => {
const result = await getHeroes()
dispatch({ type: SET_HEROES, heroes: result })
}
return <AppContext.Provider
values={{ heroes: appState.heroes, setHeroes }}
>
{props.children}
</AppContext.Provider>
}
HeroesScreen.js
const HeroesScreen = () => {
const { heroes, setHeroes } = useContext(AppContext)
useEffect(() => {
setHeroes()
}, [])
return <>
// iterate hero list
</>
}
export default HeroesScreen
以上是使用 reducer + context 作为 state 管理的简单组件设置。 英雄正在屏幕上显示,一切正常,但我收到警告Reach Hook useEffect has a missing dependency: 'setHeroes'
。 但是,如果我将它作为依赖项添加,它会使我的应用程序崩溃,并Maximum depth update exceeded
一直在搜索,但我看到的只是将 function fetch 调用放在useEffect()
中。 我想要的是提取 function 并按照 SRP 原则将其放在单独的文件中
编辑:
建议使用useCallback()
AppContext.js
const setHeroes = useCallback(() => {
getHeroes().then(result => dispatch({ type: SET_HEROES, heroes: result }))
}, [dispatch, getHeroes])
HeroesScreen.js
useEffect(() => {
setHeroes()
}, [setHeroes])
添加getHeroes
作为对useCallback
显示不必要的依赖
如果您查看AppProvider
中的代码,您会在每次呈现时创建一个新的setHeroes
function。 因此,如果您将setHeroes
作为依赖项添加到useEffect
,则代码将执行如下操作:
AppProvider
渲染, setHeroes
被创建,state 是初始 stateHeroesScreen
渲染的组件层次结构中的某处。 调用useEffect
进而调用setHeroes
getHeroes
并发送一个动作AppProvider
重新渲染的 stateAppProvider
渲染, setHeroes
是从头创建的setHeroes
发生了变化, useEffect
再次执行,整个循环永远重复! 要解决此问题,您确实需要将setHeroes
作为依赖项添加到useEffect
,然后使用useCallback
将其包装:
const setHeroes = useCallback(async () => {
const result = await getHeroes();
dispatch({ type: SET_HEROES, heroes: result });
}, [getHeroes, dispatch]);
好问题,我也有这个问题,这是我的解决方案。 我正在使用 Typescript 但它也只能与 JS 一起使用。
UserProvider.tsx
import * as React from 'react'
import { useReducer, useEffect } from 'react'
import UserContext from './UserContext'
import { getUser } from './actions/profile'
import userReducer, { SET_USER } from './UserReducer'
export default ({ children }: { children?: React.ReactNode }) => {
const [state, dispatch] = useReducer(userReducer, {})
const getUserData = async () => {
const { user } = await getUser()
dispatch({ type: SET_USER, payload: { ...user } })
}
useEffect(() => {
getUserData()
}, [])
return (
<UserContext.Provider value={{ user: state }}>
{children}
</UserContext.Provider>
)
}
然后用提供者包装你的应用程序
index.tsx
<UserProvider>
<App />
</UserProvider>
然后使用上下文消费者我这样做
AnyComponent.tsx
<UserConsumer>
{({ user }) => {
...
}}
</UserConsumer
或者你也可以像这样使用它
const { user } = useContext(UserContext)
让我知道它是否有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.