React自定义Hook异常:多组件使用时回调同时触发求助
问题分析
你的useBase64 Hook存在两个核心问题导致异常:
- 全局状态共享冲突:Hook依赖Redux全局的
state.file.file状态,当任意组件触发文件选择并更新该状态后,所有使用该Hook的组件(Avatar和Cover)都会因file变化触发useEffect,进而执行各自的回调,导致两张图片同时渲染。 - Ref传递错误:直接传递
buttonRef.current给Hook,组件初始化时ref.current为null,且ref更新不会触发组件重渲染,会导致Hook无法及时绑定点击事件。
修复方案
重构useBase64 Hook,让每个实例独立管理文件输入,不再依赖全局状态:
修复后的useBase64 Hook
import { useEffect, useRef } from 'react'; export const useBase64 = ( triggerRef: React.RefObject<HTMLButtonElement>, fn?: (base64Image: string) => void ) => { // 每个Hook实例独立维护文件输入元素 const fileInputRef = useRef<HTMLInputElement | null>(null); useEffect(() => { if (!triggerRef.current) return; // 创建独立的隐藏文件输入 if (!fileInputRef.current) { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.style.display = 'none'; document.body.appendChild(input); fileInputRef.current = input; // 监听当前实例的文件选择事件 input.addEventListener('change', (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (!file || !fn) return; const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { fn(reader.result as string); // 重置输入,允许重新选择同一张图片 input.value = ''; }; }); } const handleClick = () => { fileInputRef.current?.click(); }; triggerRef.current.addEventListener('click', handleClick); return () => { triggerRef.current?.removeEventListener('click', handleClick); // 组件卸载时清理创建的输入元素 if (fileInputRef.current) { document.body.removeChild(fileInputRef.current); fileInputRef.current = null; } }; }, [triggerRef, fn]); };
修改组件调用方式
将传递buttonRef.current改为传递ref对象本身:
Avatar组件修改
// ...其他代码 const buttonRef = useRef<HTMLButtonElement>(null); // 传递ref对象而非current useBase64(buttonRef, (image) => { console.log("avatar triggered"); dispatch(openImage({ image, type: "avatar" })); }); // ...其他代码
Cover组件修改
// ...其他代码 const buttonRef = useRef<HTMLButtonElement>(null); // 传递ref对象而非current useBase64(buttonRef, (image) => { console.log("cover triggered"); dispatch(openImage({ image, type: "cover" })); }); // ...其他代码
修复说明
- 独立实例隔离:每个
useBase64Hook创建自己的文件输入元素,避免多个组件共用全局输入导致的冲突。 - 移除全局依赖:不再依赖Redux的全局
file状态,只有当前点击组件对应的Hook会响应文件选择并触发回调。 - 正确Ref传递:传递ref对象确保
useEffect能追踪ref变化,及时绑定/解绑事件。 - 自动清理资源:组件卸载时移除创建的文件输入元素,避免内存泄漏。
内容的提问来源于stack exchange,提问作者WD_Abdulrahman
相关产品推荐
相关产品推荐

