I have already created a HOC in my react app following this, and its working fine. However i was wondering if there is a way to create a HOC as functional component(With or without state)??? since the given example is a class based component.
Tried to find the same over web but couldn't get anything. Not sure if thats even possible?? Or right thing to do ever??
Any leads will be appreciated :)
They are the pattern that emerges from React's compositional nature. The component transforms props into UI, and a higher-order component converts a component into another component. The examples of HOCs are Redux's connect and Relay's createContainer.
Higher-Order Components enable us to apply functional programming principles on components by embracing composition. React Hooks, in contrast, transformed pure (in the sense of functional programming) function components to stateful/side-effect burdened beasts. Anyway, both have their right to exist.
Higher order components are JavaScript functions used for adding additional functionalities to the existing component. These functions are pure, which means they are receiving data and returning values according to that data. If the data changes, higher order functions are re-run with different data input.
Pure Components — functional components that performs shouldComponentUpdate() automatically. Re-renders only if state or props is different from previous state or props. Higher-Order Components — functions that return one or multiple components, depending on array data.
I agree with siraj, strictly speaking the example in the accepted answer is not a true HOC. The distinguishing feature of a HOC is that it returns a component, whereas the PrivateRoute
component in the accepted answer is a component itself. So while it accomplishes what it set out to do just fine, I don't think it is a great example of a HOC.
In the functional component world, the most basic HOC would look like this:
const withNothing = Component => ({ ...props }) => ( <Component {...props} /> );
Calling withNothing
returns another component (not an instance, that's the main difference), which can then be used just like a regular component:
const ComponentWithNothing = withNothing(Component); const instance = <ComponentWithNothing someProp="test" />;
One way to use this is if you want to use ad-hoc (no pun intended lol) context providers.
Let's say my application has multiple points where a user can login. I don't want to copy the login logic (API calls and success/error messages) across all these points, so I'd like a reusable <Login />
component. However, in my case all these points of login differ significantly visually, so a reusable component is not an option. What I need is a reusable <WithLogin />
component, which would provide its children with all the necessary functionality - the API call and success/error messages. Here's one way to do this:
// This context will only hold the `login` method. // Calling this method will invoke all the required logic. const LoginContext = React.createContext(); LoginContext.displayName = "Login"; // This "HOC" (not a true HOC yet) should take care of // all the reusable logic - API calls and messages. // This will allow me to pass different layouts as children. const WithLogin = ({ children }) => { const [popup, setPopup] = useState(null); const doLogin = useCallback( (email, password) => callLoginAPI(email, password).then( () => { setPopup({ message: "Success" }); }, () => { setPopup({ error: true, message: "Failure" }); } ), [setPopup] ); return ( <LoginContext.Provider value={doLogin}> {children} {popup ? ( <Modal error={popup.error} message={popup.message} onClose={() => setPopup(null)} /> ) : null} </LoginContext.Provider> ); }; // This is my main component. It is very neat and simple // because all the technical bits are inside WithLogin. const MyComponent = () => { const login = useContext(LoginContext); const doLogin = useCallback(() => { login("[email protected]", "password"); }, [login]); return ( <WithLogin> <button type="button" onClick={doLogin}> Login! </button> </WithLogin> ); };
Unfortunately, this does not work because LoginContext.Provider
is instantiated inside MyComponent
, and so useContext(LoginContext)
returns nothing.
HOC to the rescue! What if I added a tiny middleman:
const withLogin = Component => ({ ...props }) => ( <WithLogin> <Component {...props} /> </WithLogin> );
And then:
const MyComponent = () => { const login = useContext(LoginContext); const doLogin = useCallback(() => { login("[email protected]", "password"); }, [login]); return ( <button type="button" onClick={doLogin}> Login! </button> ); }; const MyComponentWithLogin = withLogin(MyComponent);
Bam! MyComponentWithLogin
will now work as expected.
This may well not be the best way to approach this particular situation, but I kinda like it.
And yes, it really is just an extra function call, nothing more! According to the official guide:
HOCs are not part of the React API, per se. They are a pattern that emerges from React’s compositional nature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With