DEV Community

New React Hooks Pattern? Return a Component

Andrew Petersen on April 24, 2020

I recently listened to a podcast where the creator of React Router, Michael Jackson mentioned a new pattern with hooks, returning a component. ...
Collapse
 
hummingbird24 profile image
HummingBird24

Hi Andrew, Nice writeup. Question: How is the hook not creating a new Component each time the hook function executes exactly? Is it because the Panel component is created outside of the hook function itself, unlike the OP's codesandbox of the reddit link you shared?

Collapse
 
droopytersen profile image
Andrew Petersen

Yup exactly. The hook doesn't create an instance of the Panel component. The hook passes back a component definition and the props that should be passed to that component when an instance is created.

There isn't a ton of value in sending back the component definition, but it eliminates an import and I like that it takes the guess work out of which component I should use. It feels kind of like the Compound Component pattern

Collapse
 
gillibrand profile image
Jay Gillibrand

I think you could safely:

return {
  Panel: <Panel {...panelProps}/>,
  // others
}
Enter fullscreen mode Exit fullscreen mode

That creates a new Panel element, but doesn't redefine the Panel function (the component definition). The problem in the linked Reddit post is that defines its component, Modal, in the hook itself.

If you did this, your hook would need to accept any children of the panel, but wouldn't need to export panelProps. So it may or may not be syntactic a win. But it should perform the same, right?

Collapse
 
stoic25 profile image
Lambert M.

yeah, I really want to know this too

Collapse
 
devinrhode2 profile image
Devin Rhode

I guess doing <Panel {...panelProps} or {renderPanel({ props... is the way to go. One major drawback with returning a variety of components from a hook, is that you lose out on tree shaking.

Collapse
 
phelpa profile image
Plínio Helpa

I just used your article to create a hook that returns a textEditor (the JSX) and the functions that my component needs.

My line of thought was : If the JSX is tied to the the functions, it makes more sense to use a hook that gives everything instead of importing the JSX separately.

Collapse
 
droopytersen profile image
Andrew Petersen • Edited

Yeah that's where I landed too. If the hook is coupled to the UI any way, why not? One thing I've been noodling on lately is flipping it. Where component has the hook tacked on it.

import Modal from "./Modal"
// then use the hook like 
let modal = Modal.useModal({ ...stuff } )

// and use the component like 
<Modal {...modal.modalProps }>
  ....
</Modal>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
droopytersen profile image
Andrew Petersen

I took the idea described above and tried to capture it in in a new blog post, Compound Components with a Hook

Collapse
 
timminata profile image
Timothy Trewartha

This was a great read and helped me with an issue I was facing. That said, splitting the component out from the body of the hook is a lot more hassle in my case and leads to some ugly code. I would love the simplicity of having it in the function body without the performance hit of it recreating every time. Thanks for the article and the link to reddit!

Collapse
 
danieltech99 profile image
Daniel

Good idea and good article. Stumbled upon this while thinking about doing the same thing. Ended up implementing my own hook that returned a component. But then stumbled upon the React documentation for render props.

What do you think? Does this have any advantage over render props?

Fixes the concern of hooks being more logic focused and components containing view related stuff.

Collapse
 
droopytersen profile image
Andrew Petersen • Edited

Render props definitely have their uses, but in this situation I was trying to account for a situation where you want to hoist the behavior of a component (the Panel's open and close methods) to the parent, without having to fully manage that state in the parent component.

The issue with render props is that the "state" that is provided to the render function is not available to you in the parent component. In the example above, the Panel component could provide openPanel, closePanel, isOpen to a render function (or render as children or w/e). But then what if I want to have a button somewhere else on the page to trigger the panel opening? The openPanel is only available inside the render prop function.

Collapse
 
terpimost profile image
Vlad Korobov

Thank you for this article

Collapse
 
seanmclem profile image
Seanmclem

I really like this way of using hooks

Collapse
 
limdev profile image
lim-dev

Cool concept - why doesn't the onRenderNavigation prop need to be memoized?

Collapse
 
droopytersen profile image
Andrew Petersen

You could. But that is used to render what is basically a leaf node component that is very small. So the cost of memoizing and checking for changes could be higher than letting the react reconciler diff the vdom.

Collapse
 
nicoguyon profile image
Nicolas Guyon

Thank you for this article.

That was exactly what I was thinking of and wondering if it was possible or eventually a bad idea. I intend to remove a lot of boilerplate now :)