React useEffect仅注册一次时如何获取最新useState状态值
你遇到的问题本质是闭包陷阱:首次注册的事件回调捕获了初始渲染时的serviceName变量,后续状态更新后,已经注册的回调闭包不会同步更新,因此始终读取到初始值。由于API限制无法重复注册事件,你可以通过useRef存储可变的最新值/逻辑来绕开闭包限制。
方案1:通过ref同步指定状态值(逻辑更直观)
仅针对需要用到的serviceName状态做同步,适合逻辑简单、后续不会新增状态依赖的场景:
const DefaultRateSection: React.FC<{ registerOnSaveEvent: (fn: () => void) => void; }> = ({registerOnSaveEvent}) => { const [serviceName, setServiceName] = useState('the name'); const serviceNameInput = useRef<HTMLInputElement>(null); // 新增ref存储最新的serviceName值 const latestServiceNameRef = useRef(serviceName); // serviceName更新时同步刷新ref内容 useEffect(() => { latestServiceNameRef.current = serviceName; }, [serviceName]) useEffect( () => { return registerOnSaveEvent(() => { // 从ref读取最新值,绕开闭包捕获限制 if (!latestServiceNameRef.current) { serviceNameInput.current?.focus(); } }); }, [registerOnSaveEvent] ); return ( <input ref={serviceNameInput} value={serviceName} onChange={(event) => { const newValue = event.target.value; setServiceName(newValue) }} /> ); };
方案2:通过ref同步完整处理逻辑(扩展性更好)
直接存储最新的事件处理函数,后续逻辑新增任意状态依赖都不需要额外加ref同步:
const DefaultRateSection: React.FC<{ registerOnSaveEvent: (fn: () => void) => void; }> = ({registerOnSaveEvent}) => { const [serviceName, setServiceName] = useState('the name'); const serviceNameInput = useRef<HTMLInputElement>(null); // 新增ref存储最新的处理函数 const latestSaveHandlerRef = useRef<() => void>(); // 每次渲染更新处理函数,天然能拿到所有最新状态 latestSaveHandlerRef.current = () => { if (!serviceName) { serviceNameInput.current?.focus(); } } useEffect( () => { return registerOnSaveEvent(() => { // 仅调用ref中存储的最新逻辑,本身不需要更新 latestSaveHandlerRef.current?.(); }); }, [registerOnSaveEvent] ); return ( <input ref={serviceNameInput} value={serviceName} onChange={(event) => { const newValue = event.target.value; setServiceName(newValue) }} /> ); };
注意事项
由于registerOnSaveEvent没有提供注销方法,若该事件是全局级别的,组件卸载后仍会触发你注册的回调,可能会访问已卸载组件的ref。如果有这种场景,建议新增一个组件是否卸载的标记ref,在回调中判断标记为未卸载时再执行逻辑。
内容的提问来源于stack exchange,提问作者Jellof
相关产品推荐
相关产品推荐

