React项目注册新用户时重复生成Braintree客户ID问题
注册用户时重复生成Braintree客户ID问题排查

我正在开发一个React项目,每次注册新用户时都会生成两个Braintree客户ID,而预期应为每个用户仅生成一个。
以下是我的代码:
const ConfirmEmail = ({ handleItems, onSubmitUser }) => { let params = useParams(); let isMounted = true; const [loading, setLoading] = useState(true); useCallback(() => { fetchData(); }, [isMounted]); const fetchData = async () => { try { if (JSON.parse(localStorage.getItem("user"))) return; const response = await UserService.confirmEmail(params.tokenEmail); if (response && response.data && !response.err) { const { user, accessToken } = response.data; localStorage.setItem("accessToken", accessToken); if (isMounted) { handleItems(); onSubmitUser(user); } } if (isMounted) setLoading(false); } catch (err) { const { response } = err; if (response?.data?.data) toast.error(response.data.data); } // Unmounted return (isMounted = false); };
问题根源分析
- 错误使用
useCallback触发请求:useCallback用于缓存函数,不是处理组件初始化副作用的钩子,用它触发fetchData会导致函数重复执行。尤其isMounted是普通变量,每次组件重渲染都会重置,可能触发多次请求。 isMounted变量管理不规范:普通变量isMounted在组件重渲染时会被重新赋值为true,无法准确跟踪组件挂载状态,可能导致异步请求完成后重复执行onSubmitUser(该函数大概率包含生成Braintree客户ID的逻辑)。- 缺少请求去重逻辑:没有确保
fetchData只执行一次,组件意外重渲染时会再次发起请求,进而重复生成客户ID。
修复方案
改用useEffect处理初始化请求,配合useRef管理挂载状态,确保请求仅执行一次:
const ConfirmEmail = ({ handleItems, onSubmitUser }) => { const params = useParams(); const [loading, setLoading] = useState(true); // 用Ref跟踪组件挂载状态,避免重渲染重置 const isMountedRef = useRef(true); useEffect(() => { const fetchData = async () => { try { // 本地已有用户数据时直接终止,避免重复请求 const storedUser = localStorage.getItem("user"); if (storedUser && JSON.parse(storedUser)) { setLoading(false); return; } const response = await UserService.confirmEmail(params.tokenEmail); if (response && response.data && !response.err) { const { user, accessToken } = response.data; localStorage.setItem("accessToken", accessToken); // 确保组件仍挂载时才执行后续操作 if (isMountedRef.current) { handleItems(); onSubmitUser(user); } } } catch (err) { const { response } = err; if (response?.data?.data) toast.error(response.data.data); } finally { // 无论请求成功失败,都更新loading状态(仅组件挂载时) if (isMountedRef.current) setLoading(false); } }; fetchData(); // 组件卸载时更新挂载状态 return () => { isMountedRef.current = false; }; }, [params.tokenEmail, handleItems, onSubmitUser]); // 依赖数组确保只有必要时重新执行 // 组件渲染逻辑... };
关键修改点说明
- 用
useEffect替代useCallback:useEffect专门处理组件挂载/更新时的副作用,确保初始化请求只执行一次(除非依赖变化)。 useRef管理挂载状态:useRef的current属性在组件生命周期内保持稳定,不会因重渲染重置,能准确判断组件是否还在挂载中。- 增加请求终止逻辑:本地已有用户数据时直接终止请求,避免重复执行后续流程。
finally块统一处理loading状态:确保无论请求成功或失败,都能正确更新loading状态,同时避免在组件卸载后更新状态。
内容的提问来源于stack exchange,提问作者Jabc
相关产品推荐
相关产品推荐

