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

React useState首次渲染无法访问正确属性值 打印对象与属性值不一致

问题根因

  1. 异步逻辑处理错误:你在mergeCookiesWithUserState函数内部调用了异步的getCookieValue读取Cookie,但没有等待异步操作完成就直接返回了状态对象,函数返回时Cookie还没读取完成,返回的状态还是初始默认值。
  2. 直接修改不可变对象:异步读取Cookie完成后,你直接修改了已经返回的状态对象属性,而没有通过React的setState方法更新状态,React不会感知到这次修改,也不会触发组件重渲染,所以组件拿到的始终是初始值。
  3. 控制台输出的特性误导:Chrome控制台打印对象时保存的是对象的引用,你点击展开对象时才会读取对象的当前属性值,此时异步Cookie读取已经完成,你修改了原对象的属性,所以展开看到的是正确值;而打印具体属性(数字类型)时输出的是执行log那一刻的即时值,当时异步操作还没完成,所以拿到的是默认的0。

修复方案

  1. 不要在同步的状态初始化阶段处理异步逻辑,改为在useEffect中读取Cookie,读取完成后调用setUserState更新状态触发重渲染。
  2. 禁止直接修改React状态对象,所有状态更新必须通过setState完成。

修改后的自定义Hook参考代码

import { useState, useCallback, useEffect } from 'react'
import { UserContextProps, UserStateProps, USER_STATE_DEFAULT } from '../context/UserContext'
import {
  COOKIE_NAME_LANGUAGE,
  COOKIE_NAME_ADULT_FILTER,
  COOKIE_NAME_NEW_TAB,
  COOKIE_NAME_SEARCH_COUNT,
  getCookieValue,
  setCookie,
} from '../helpers/_cookies'

const cookiesName: { [key in keyof UserStateProps]?: any } = {
  numOfSearches: COOKIE_NAME_SEARCH_COUNT,
  language: COOKIE_NAME_LANGUAGE,
  adultContentFilter: COOKIE_NAME_ADULT_FILTER,
  openInNewTab: COOKIE_NAME_NEW_TAB,
}

export const useUserStateSyncedWithCookies = (): UserContextProps => {
  // 初始状态直接用默认值
  const [userState, _setUserState] = useState<UserStateProps>(USER_STATE_DEFAULT)

  const setUserState = useCallback((nextState: Partial<UserStateProps>): void => {
    _setUserState((prevState) => {
      const newState = {
        ...prevState,
        ...nextState,
      }
      newState.adultContentFilter = Number(newState.adultContentFilter)
      newState.language = Number(newState.language)

      for (const key in nextState) {
        if (Object.prototype.hasOwnProperty.call(cookiesName, key)) {
          setCookie(cookiesName[key], newState[key], { expires: 365 })
        }
      }

      return newState
    })
  }, [])

  // 组件挂载后异步读取Cookie更新状态
  useEffect(() => {
    const fetchAllCookies = async () => {
      const statePatch: Partial<UserStateProps> = {}
      // 并行读取所有需要同步的Cookie
      await Promise.all(
        Object.entries(cookiesName).map(async ([stateKey, cookieName]) => {
          const cookieValue = await getCookieValue(cookieName)
          if (cookieValue !== undefined) {
            statePatch[stateKey as keyof UserStateProps] = cookieValue
          }
        })
      )
      // 读取完成后统一更新状态,触发重渲染
      if (Object.keys(statePatch).length > 0) {
        setUserState(statePatch)
      }
    }
    fetchAllCookies()
  }, [setUserState])

  return {
    userState,
    setUserState,
  }
}

额外说明

如果组件需要等待Cookie读取完成再渲染,可以加一个加载状态,避免组件拿到初始默认值执行错误逻辑。


内容的提问来源于stack exchange,提问作者Giorgia Sambrotta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 21:39:04