You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件在useCurrentUser Hook值更新时未重渲的问题排查

问题:React中currentUser更新时Main组件不重新渲染

问题概述

在React应用(使用Supabase作为数据库)中出现如下问题:当useCurrentUser() Hook返回的currentUser值变化时,Main组件仅初始渲染一次,后续不再触发重新渲染;但其他使用useCurrentUser()的组件能正常显示更新后的currentUser值,且Main组件中监听currentUser的useEffect仅打印一次currentUser。

相关代码

Main组件

export default function Main() {
  const [portrait, setPortrait] = useState(false)
  const [landscape, setLandscape] = useState(false)
  const [loginVisible, setLoginVisible] = useState(true)
  const screenRef = useRef()
  const em = useEm()
  const currentUser = useCurrentUser()

  useEffect(() => {
    console.log(currentUser)
    if (currentUser?.id) {
      setLoginVisible(false)
    }
  }, [currentUser])

  const version = 'v 2.0.0'

  return (
    <SupabaseBoardStateContextProvider sessionId="asdf">
      <View
        style={{ justifyContent: 'center', alignItems: 'center', height: '100%', width: '100%', overflow: 'hidden', flexBasis: 0, flexGrow: 1 }}
        ref={screenRef}
        onLayout={() =>
          screenRef.current.measure((x, y, w, h) => {
            if (w > h) {
              setLandscape(true)
              setPortrait(false)
            }
            if (h > w) {
              setLandscape(false)
              setPortrait(true)
            }
          })
        }
      >
        {portrait && <Portrait />}
        {landscape && <Landscape />}
        {loginVisible && <Login close={() => setLoginVisible(false)} />}
      </View>
    </SupabaseBoardStateContextProvider>
  )
}

useCurrentUser Hook

function useCurrentUser() {
  return useContext(CurrentUserContext)
}

useSupabaseUsers Hook

function useSupabaseUsers() {
  const [users, setUsers] = useState([])
  const [currentUser, setCurrentUser] = useState({})
  const [userChannel, setUserChannel] = useState()

  async function checkAnonymousId() {
    let anon = ''
    const [[, oldAnon], [, displayName]] = await AsyncStorage.multiGet(['@anonymous_user_id', '@anonymous_user_displayname'])

    anon = oldAnon
    if (!oldAnon) {
      const newAnon = uuid.v4()
      AsyncStorage.setItem('@anonymous_user_id', newAnon)
      anon = newAnon
    }

    const channel = supabase.channel('*')
    channel
      .on('presence', { event: 'sync' }, () => {
        const state = channel.presenceState()
        const keys = Object.values(state).map(([user]) => user)

        setCurrentUser(keys.filter(x => x.anon_key === anon)[0])
        setUsers(keys)
      })
      .subscribe(async status => {
        if (status === 'SUBSCRIBED') {
          await channel.track({ anon_key: anon, anon_displayName: displayName })

          supabase.auth.getUser().then(handleAuth)

          supabase.auth.onAuthStateChange(() => {
            supabase.auth.getUser().then(handleAuth)
          })
        }
      })

    function handleAuth({ data }) {
      if (data) channel.track({ anon_key: anon, anon_displayName: displayName, id: data.user.id, name: data.user.user_metadata.name })
    }

    setUserChannel(channel)
  }

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

  return [users, currentUser, userChannel]
}

已尝试的排查步骤

  • 多次重构useCurrentUser() Hook,并尝试将其放入ContextProvider,问题仍存在。
  • 确认其他组件可正常获取更新后的currentUser,排除数据库层面问题。
  • 尝试通过解构赋值创建新对象,未解决问题。

问题分析与修复方案

核心问题

  1. ContextProvider范围错误:Main组件没有被CurrentUserContext.Provider包裹,导致它无法感知Context中currentUser的变化。其他能正常更新的组件应该处于该Provider的包裹范围内,所以能收到更新通知,而Main组件作为外层组件,不在监听范围内,自然无法触发重渲染。
  2. 空值处理不当:useSupabaseUsers中初始化currentUser为{},当用户未登录时keys.filter(...)可能返回undefined,此时setCurrentUser会把状态设为undefined,与初始状态类型不一致,可能引发状态更新异常。

修复步骤

  1. 调整ContextProvider包裹范围:确保Main组件被CurrentUserContext.Provider包裹,示例如下:
// 入口文件或Main的父组件中
function App() {
  const [users, currentUser, userChannel] = useSupabaseUsers()
  
  return (
    <CurrentUserContext.Provider value={currentUser}>
      <Main />
    </CurrentUserContext.Provider>
  )
}
  1. 处理currentUser空值情况:在useSupabaseUsers的sync事件逻辑中,避免将currentUser设为undefined,可以保留当前状态或设置默认空对象:
// 修改sync事件处理代码
const matchedUser = keys.filter(x => x.anon_key === anon)[0]
setCurrentUser(matchedUser || currentUser) // 无匹配用户时保留当前状态
  1. 确保状态引用更新:React通过浅比较判断状态是否变化,若matchedUser是同一个对象引用,即使内部属性变化也不会触发重渲染,可以通过解构赋值生成新对象:
setCurrentUser(matchedUser ? {...matchedUser} : currentUser)

内容的提问来源于Stack Exchange,提问作者Aaron Gayle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 17:05:06