React函数式组件state变化时无法触发重渲染该如何解决?
问题根因
- 负责拉取用户信息的
useEffect依赖项为空数组,仅会在组件首次挂载时执行1次。用户首次登录前组件已经完成挂载,登录后回到页面不会重新触发接口请求,因此拿不到最新的用户信息。 - 你将
dataRes状态变化后的处理逻辑直接写在组件顶层渲染流程中,还在逻辑内直接调用setState,属于渲染期间更新状态的错误写法,不仅会触发不必要的重渲染,还可能出现状态更新不及时、逻辑不触发的问题。 - 负责存储用户名的
username状态被错误定义为布尔类型,和实际用户名做对比的判断逻辑永远不符合预期,导致登录状态更新失败。 - 额外的1秒强制关闭loading的
useEffect逻辑不合理,很容易出现接口还未返回就提前结束加载,导致登录状态还没更新就渲染出登录按钮的问题。
修复后代码
function Username() { const cookies = new Cookies(); const [username, setUsername] = useState(null); const [loading, setLoading] = useState(true); const [shortened, setShortened] = useState(false); const authLink = "https://discord.com/api/oauth2/authorize?client_id=" + clientID + "&redirect_uri=" + clientRedirect + "&response_type=code&scope=identify%20guilds"; const [loggedin, setLoggedin] = useState(false); const history = useHistory(); const [data, setData] = useState(null); const userToken = cookies.get("someCookie"); // 监听token变化,有token时自动拉取用户信息 useEffect(() => { if (!userToken) { setLoading(false); return; } setLoading(true); fetch('SomeAPIUrlHere', { method: "POST", headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: userToken }) }) .then((res) => res.json()) .then((data) => { setData(data); if (data?.username) { setUsername(data.username); setShortened(data.username.length > 10); setLoggedin(true); } setLoading(false); }) .catch(err => { console.log(err); setLoading(false); }) }, [userToken]); const SignOut = () => { cookies.remove("us_au_tok"); history.push("/"); window.location.reload(false); } return( <div className="flex float-left"> <a href={loggedin === false ? authLink : "/dashboard"} className={loggedin === false ? "shadow-2xl text-center text-white bg-gray rounded-2xl p-2 lg:px-5 md:px-5 sm:pl-3.5 xs:pl-3.5 mr-2 block lg:w-40 md:w-40" : "text-center text-white rounded-2xl p-2 lg:px-5 md:px-5 sm:pl-3 xs:pl-3 mr-2 block lg:w-48 md:w-48"}> <div className={loading === true ? "md:w-8 lg:w-8 sm:w-7 xs:w-7 m-auto text-center" : "m-auto text-center"}> <div className="flex justify-nter"> {loggedin === true ? <img src={`https://cdn.discordapp.com/avatars/${data.userid}/${data.avatar}.png?size=1024`} alt="Useravatar" className="lg:max-h-50 md:max-h-50 sm:max-h-35 xs:max-h-35 rounded-full mr-2"></img> : ""} {loading === true ? <img src={LoadingSVG} alt='Loading Animation'/> : <p className="hover:text-brightgray duration-300 lg:m-auto md:m-auto sm:my-auto xs:my-auto">{loggedin === true ? <span>{shortened === false ? username : username.substring(0, 10) + "..."}</span> : "Login"}</p> } </div> </div> </a> {loggedin === true ? <button onClick={SignOut}><FontAwesomeIcon icon={faSignOutAlt} className="text-white mr-4" title="Sign Out" /></button> : ""} </div> ) };
核心修改点
- 给拉取用户信息的
useEffect增加了userToken作为依赖项,登录后token生成/变化时会自动重新触发请求拉取用户信息,不需要手动刷新页面 - 删除了顶层渲染流程中的状态更新逻辑,将用户信息处理全部挪到接口请求回调中,避免渲染期间更新状态的问题
- 修复了
username状态的类型错误,改为存储实际用户名字符串,逻辑判断符合预期 - 移除了不合理的1秒强制关闭loading逻辑,loading状态完全由接口请求流程控制,不会出现接口未返回就结束加载的问题
内容的提问来源于stack exchange,提问作者Aqua
相关产品推荐
相关产品推荐

