简体   繁体   English

对 React 上下文的 state 的更新不会重新渲染子项

[英]Updates to the state of React context don't re-render children

I'm updating the shared state in context through a callback in the child component, but that does not cause a re-render, which results in the context in the child component having an initial value until the next re-render.我正在通过子组件中的回调在上下文中更新共享的 state,但这不会导致重新渲染,这会导致子组件中的上下文在下一次重新渲染之前具有初始值。

Is there a way to force the update of the child and a re-render once state is updated in the context provider?一旦 state 在上下文提供程序中更新,是否有办法强制更新子项并重新渲染?

My context provider:我的上下文提供者:

const UserLocationContext = React.createContext()

export const useUserLocation = () => {
    return useContext(UserLocationContext)
}

export const UserLocationProvider = ({ children }) => {
    const [ipUserLocation, setIpUserLocation] = useState(null)

    const updateIpUserLocation = (ipUserLocation) => {
        setIpUserLocation(ipUserLocation)
        console.log(ipUserLocation) //value is updated here immediately after the updateIpUserLocation call
    }
    return (
        <UserLocationContext.Provider value = {{ipUserLocation, updateIpUserLocation}}>
            {children}
        </UserLocationContext.Provider>
    )

}

export default UserLocationProvider

Child:孩子:

const LocationHandler = () => {
    const {ipUserLocation, updateIpUserLocation} = useUserLocation()
    
    useEffect(() => {
    const ip_url = `https://api.freegeoip.app/json/`
    const fetchIPLocation = async () => { 
        
        const result = await fetch(ip_url);
        const json = await result.json();
        updateIpUserLocation([json.latitude, json.longitude])
        console.log(ipUserLocation) //value here remains null until next re-render
    }
    fetchIPLocation()

    }, []);}

The problem is useState is asynchronous, so ipUserLocation value is not updated immediately after setIpUserLocation gets called.问题是useState是异步的,因此在调用setIpUserLocationipUserLocation值不会立即更新。

For the fix, you can add ipUserLocation as a dependency to useEffect that would help you to listen to all changes from ipUserLocation on LocationHandler .对于修复,您可以将ipUserLocation添加为useEffect的依赖项,这将帮助您监听LocationHandleripUserLocation的所有更改。

const LocationHandler = () => {
    const {ipUserLocation, updateIpUserLocation} = useUserLocation()
    
    useEffect(() => {
    const ip_url = `https://api.freegeoip.app/json/`
    const fetchIPLocation = async () => { 
        
        const result = await fetch(ip_url);
        const json = await result.json();
        updateIpUserLocation([json.latitude, json.longitude])
    }
    fetchIPLocation()

    }, []);}

    //add another `useEffect` with `ipUserLocation` in dependencies
    useEffect(() => {
       //TODO: You can do something with updated `ipUserLocation` here
       console.log(ipUserLocation)
    }, [ipUserLocation])

    return ...
}

Actually the child component gets re-rendered when you update the state of the context.实际上,当您更新上下文的 state 时,子组件会重新呈现。 And this happens because you are using the useContext hook to listen to any change made to the context.发生这种情况是因为您正在使用useContext挂钩来监听对上下文所做的任何更改。 What you can do to actually prove to yourself that the child gets re-rendered is adding this in the child component:你可以做些什么来向自己证明孩子被重新渲染是在子组件中添加它:

useEffect(() => {
 console.log(ipUserLocation);
}, [ipUserLocation])

With this useEffect the console.log will run everytime the child gets re-rendered and the ipUserLocation has changed.使用此 useEffect, console.log将在每次重新渲染子项并且ipUserLocation已更改时运行。

Docs: https://reactjs.org/docs/hooks-effect.html文档: https://reactjs.org/docs/hooks-effect.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM