React类组件HOC正常运行,Hook函数式HOC失效,求问题排查
Hey there! Let's break down why your function-based HOC isn't working as expected, and fix it step by step.
The Core Problem: Duplicate Axios Interceptors
In your class component version, you set up interceptors once in componentWillMount (which runs only when the component mounts). But in the Hook version, you're creating interceptors directly in the component function body—this means every time the component re-renders, you're adding a new pair of request/response interceptors to your axios instance.
Over time, this builds up duplicate interceptors: each one tries to update the error state, leading to inconsistent behavior (like errors not clearing properly, or multiple modals popping up). Plus, the cleanup logic in your useEffect won't catch all these duplicate interceptors, causing memory leaks.
Additional Small Issues
- You weren't passing the
errorprop to theModalcomponent (like you did in the class version), which might break any logic inside the Modal that relies on it. - In the response interceptor, you were setting
errorto the fullerrobject instead oferr.response—mismatching the class component's behavior.
Fixed Hook-Based HOC
Here's the corrected code, with explanations:
import React, { useState, useEffect } from "react"; import Modal from "../components/UI/Modal/Modal"; const withErrorHandler = (WrappedComponent, axios) => { return (props) => { const [error, setError] = useState(null); // Set up interceptors ONCE when the component mounts useEffect(() => { const requestInterceptor = axios.interceptors.request.use((req) => { setError(null); return req; }); const responseInterceptor = axios.interceptors.response.use( (res) => res, (err) => { // Match class component behavior: store the response error setError(err.response); } ); // Cleanup: Remove interceptors when the component unmounts return () => { axios.interceptors.request.eject(requestInterceptor); axios.interceptors.response.eject(responseInterceptor); }; }, [axios]); // Depend on axios to reset interceptors if the instance changes const errorConfirmedHandler = () => { setError(null); }; return ( <React.Fragment> <Modal showModal={!!error} error={error} // Restore the error prop for Modal closeModal={errorConfirmedHandler} > {error ? error.message : null} </Modal> <WrappedComponent {...props} /> </React.Fragment> ); }; }; export default withErrorHandler;
Key Fixes Explained
- Interceptor Setup in useEffect: By wrapping the interceptor creation in
useEffectwith a dependency array containingaxios, we ensure interceptors are only created once when the component mounts (or if the axios instance changes). This prevents duplicate interceptors from piling up. - Proper Cleanup: The cleanup function inside
useEffectruns when the component unmounts, removing the interceptors just likecomponentWillUnmountdid in the class version—no more memory leaks. - Matching Error Handling: We now set
errortoerr.response(same as the class component) and pass theerrorprop to Modal, ensuring consistent behavior across both versions. - Simplified
showModal: Using!!erroris a cleaner way to convert the error state to a boolean, instead of the ternary check.
This should make your Hook-based HOC work exactly like the class component version!
内容的提问来源于stack exchange,提问作者dracarys

