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 +上下文作为状态管理的组件的简单设置。屏幕上显示了英雄,一切正常,但我收到警告Reach Hook useEffect has a missing dependency: 'setHeroes'
。但是,如果我将其添加为依赖项,则会使应用Maximum depth update exceeded
经过搜索,但我所看到的只是将函数fetch调用放在useEffect()
内。我想按照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
的依赖项,则linter显示不必要的依赖项
答案 0 :(得分:3)
如果您查看AppProvider
中的代码,则每次渲染时都会创建一个新的setHeroes
函数。因此,如果您将setHeroes
添加为对useEffect
的依赖,则代码将执行以下操作:
AppProvider
渲染,创建setHeroes
,状态为初始状态HeroesScreen
下的某个位置进行渲染。调用useEffect
,依次调用setHeroes
getHeroes
被调用并调度一个动作AppProvider
重新渲染的状态AppProvider
渲染,setHeroes
从头开始创建useEffect
更改后,setHeroes
再次执行,整个循环永远重复!要解决此问题,您确实确实需要将setHeroes
作为对useEffect
的依赖项,然后使用useCallback
包装它:
const setHeroes = useCallback(async () => {
const result = await getHeroes();
dispatch({ type: SET_HEROES, heroes: result });
}, [getHeroes, dispatch]);
答案 1 :(得分:1)
很好的问题,我也遇到了这个问题,这是我的解决方案。我正在使用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>
然后使用Context Consumer我这样做
AnyComponent.tsx
<UserConsumer>
{({ user }) => {
...
}}
</UserConsumer
或者您也可以像这样使用它
const { user } = useContext(UserContext)
让我知道它是否有效。