React中如何使用useEffect和useCallback触发state hook重新渲染?
问题根源
你遇到的点击两次才触发请求的核心原因是React状态更新的异步特性:在同一个事件处理函数的执行上下文里,调用setState后不会立刻修改当前作用域下的状态变量,状态更新会被批量推入队列,等到当前事件回调执行完毕后才会触发重渲染,重渲染后才能拿到最新的状态值。
你在submit函数里刚调用setKeyValid(true),紧接着判断if (keyValid === true),此时拿到的keyValid还是本次渲染初始的旧值(false),所以判断条件不生效,只有第二次点击的时候,keyValid已经是上一次渲染更新后的true,才会走数据库请求的逻辑。你之前看到useEffect里打印的keyValid是更新后的值,是因为useEffect会在重渲染完成后执行,此时状态已经更新完成,属于React的正常执行时序。
另外你的代码还存在几个可优化的问题:
- 冗余的
useCallback封装,且依赖项配置错误:updateKeyValid完全没有存在的必要,直接调用setKeyValid即可 - 长度校验逻辑写反:
!inputKeyRef.current.value.length < 12的运算优先级不符合校验需求,无法正确判断长度范围 - 混用
ref和状态读取输入值,没有必要,表单输入直接绑定状态更符合React开发规范 Button组件的onClick属性多写了一个闭合大括号,属于语法错误
解决方案
方案1:直接在验证通过后发起请求(最简实现)
不需要等keyValid状态更新,校验通过后直接执行数据库请求逻辑,同时更新状态即可:
import { useState } from 'react' export function Form() { const[inputKey, setInputKey] = useState(""); const [keyValid, setKeyValid] = useState(false); const [keyAuthentic, setKeyAuthentic] = useState(false); // 输入框绑定onChange更新状态,你原代码缺少这部分 const handleInputChange = (e) => { setInputKey(e.target.value); } const submit = e => { e.preventDefault(); // 修正长度校验逻辑:要求密钥长度在12-20位之间 const keyLength = inputKey.length; const isValid = keyLength >= 12 && keyLength <= 20; setKeyValid(isValid); if (isValid) { // 校验通过直接发请求,不用等待状态更新 db.collection("users").doc(inputKey).get().then((doc) => { if (doc.exists) { setKeyAuthentic(true) console.log(doc.data()) } }); } }; return ( <div> {/* 输入框绑定状态 */} <input value={inputKey} onChange={handleInputChange} /> <div className="form__submit"> <Button type="submit" onClick={submit}>Submit</Button> </div> </div> ) } export default Form
方案2:通过useEffect监听状态触发请求
如果你的业务逻辑要求必须等keyValid状态更新后再触发请求,就把数据库请求逻辑放到useEffect中,监听keyValid变化自动执行:
import { useState, useEffect } from 'react' export function Form() { const[inputKey, setInputKey] = useState(""); const [keyValid, setKeyValid] = useState(false); const [keyAuthentic, setKeyAuthentic] = useState(false); const handleInputChange = (e) => { setInputKey(e.target.value); } // 监听keyValid变化,状态更新后自动发起请求 useEffect(() => { if (keyValid) { db.collection("users").doc(inputKey).get().then((doc) => { if (doc.exists) { setKeyAuthentic(true) console.log(doc.data()) } }); } }, [keyValid, inputKey]) // 依赖项加入keyValid和inputKey,确保拿到最新值 const submit = e => { e.preventDefault(); const keyLength = inputKey.length; const isValid = keyLength >= 12 && keyLength <= 20; setKeyValid(isValid); }; return ( <div> <input value={inputKey} onChange={handleInputChange} /> <div className="form__submit"> <Button type="submit" onClick={submit}>Submit</Button> </div> </div> ) } export default Form
内容的提问来源于stack exchange,提问作者Levan Marston
相关产品推荐
相关产品推荐

