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

React子组件在Apollo Client useQuery完成后偶尔不重渲染问题

React + Apollo Client 导航栏偶尔不更新问题排查与解决

问题描述

我正在开发一个基于React、GraphQL(Apollo Client)的应用。应用加载时通过useQuery钩子发起查询获取当前用户,查询依赖localStorage中保存的Web Token做身份验证,返回的用户数据会作为props传给导航栏组件NavBar。

但现在的问题是:查询返回用户数据后,导航栏仅偶尔会更新。刷新应用10次,只有2-3次能正确重渲染导航栏。

主App组件代码

const App = () => {
    
    if (!window.Promise) {
        window.Promise = PromisePolyfill
    }

    const [token, setToken] = useState(null)

    const currentUserQuery = useQuery(GET_CURRENT_USER, {
        onCompleted: (data) => {
            console.log("user query completed", data)
        }
    }, [])
    
    return (
        <div id="page-container">
            {currentUserQuery.loading || currentUserQuery.error || currentUserQuery.data === null ? <NavBar currentUser={null} /> : <NavBar currentUser={currentUserQuery.data.me} />}
            <div id="content-wrap">
                <Link to="/users/63f6c389dadc96cc795852ab">test</Link>
                <div>
                    <Routes>
                        <Route path="/" element={<Home />} />
                        <Route path="/login" element={<LoginForm setToken={setToken} />} />
                        <Route path="/signup" element={<h2>Sign Up</h2>} />
                        <Route path="/users/:id" element={<UserProfile />} />
                        <Route path="*" element={<h2>Page Not Found</h2>} />
                    </Routes>
                </div>
            </div>
            <Footer />
        </div>
    )
}
const NavBar = ({currentUser}) => {

    console.log("current user prop in navbar", currentUser)


    if (currentUser) {
        return (
            <Navbar collapseOnSelect className="navbar" key={currentUser}>
                <Navbar.Toggle aria-controls="responsive-navbar-nav" />
                <Navbar.Collapse id="responsive-navbar-nav">
                    <Nav className="me-auto">
                        <Nav.Link href="#" as="span">
                            <Link className='link' to="/"><img className="logo" src={ccLogo} alt="Chef's Club" /></Link>
                        </Nav.Link>
                    </Nav>
                    <Nav>
                        <NavDropdown title="Dropdown" id="basic-nav-dropdown">
                            <NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
                            <NavDropdown.Item href="#action/3.2">
                                Another action
                            </NavDropdown.Item>
                            <NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
                            <NavDropdown.Divider />
                            <NavDropdown.Item href="#action/3.4">
                                Separated link
                            </NavDropdown.Item>
                        </NavDropdown>
                    </Nav>
                </Navbar.Collapse>
            </Navbar>
        )
    }

    return (
        <Navbar collapseOnSelect className="navbar" key={currentUser}>
            <Navbar.Toggle aria-controls="responsive-navbar-nav" />
            <Navbar.Collapse id="responsive-navbar-nav">
                <Nav className="me-auto">
                    <Nav.Link href="#" as="span">
                        <Link className='link' to="/"><img className="logo" src={ccLogo} alt="Chef's Club" /></Link>
                    </Nav.Link>
                </Nav>
                <Nav>
                    <Nav.Link href="#" as="span">
                        <Link className='link' to="/login">Login</Link>
                    </Nav.Link>
                </Nav>
            </Navbar.Collapse>
        </Navbar>
    )

}

补充信息

  • 从onCompleted的控制台输出可以确认,服务器每次都返回了用户数据,但导航栏有时不更新。
  • 成功场景:应用初始加载时给NavBar传null,查询返回后props变化触发重渲染。
  • 失败场景:服务器返回用户数据,但NavBar未收到更新后的props,也没有重渲染。

分析与解决方案

1. useQuery参数错误导致Hook行为不稳定

Apollo Client的useQuery钩子不接受第三个依赖数组参数(这是React自定义Hook的写法,并非useQuery的官方API)。你在调用时传递了空数组[],这违反了React Hook的规则,导致组件渲染时useQuery的状态更新逻辑偶尔失效,无法触发App组件重渲染,进而NavBar无法收到新的props。

修复方式: 移除useQuery的第三个参数,正确调用如下:

const currentUserQuery = useQuery(GET_CURRENT_USER, {
    onCompleted: (data) => {
        console.log("user query completed", data)
    }
})

2. NavBar的key属性使用不当

你给Navbar组件设置了key={currentUser},但currentUser是对象类型。React判断key是否变化时采用引用比较,如果Apollo Client缓存返回的用户对象引用出现复用(或渲染时机问题),可能导致key的变化检测失效,组件无法触发重渲染。

优化方式: 使用currentUser的唯一标识(如id)作为key,或者直接移除该key(条件渲染的两个分支React会自动处理):

// 有currentUser时用id作为key,无用户时用固定字符串
<Navbar collapseOnSelect className="navbar" key={currentUser?.id || 'anonymous-user'}>

3. 可选验证:检查Apollo Client缓存策略

如果上述修改后问题仍存在,检查Apollo Client的fetchPolicy配置。默认的cache-first策略会优先读取缓存数据,若缓存中的用户数据未及时更新,可能导致useQuery不触发网络请求或状态更新。可根据业务需求调整为network-only强制从服务器获取最新数据:

const currentUserQuery = useQuery(GET_CURRENT_USER, {
    onCompleted: (data) => {
        console.log("user query completed", data)
    },
    fetchPolicy: 'network-only' // 按需使用
})

总结

最核心的问题是useQuery错误传递了依赖数组,破坏了Hook的正常执行逻辑,导致组件偶尔无法感知查询状态变化。先移除该参数,再调整key属性,基本可以解决导航栏偶尔不更新的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 07:07:58