Who said render props are obsolete? The good old pattern is still valid for the common use case where hooks may not always be the right choice. With that said, can you guess which code snippet is more efficient and why? I started a with the same question recently, trying to understand if people have strong opinions about hooks and render props. Opposing the two is not a fantasy, but comes from a practical concern. twitter discussion : When it comes to state management render prop component is often than pure hooks solution. more flexible and less error-prone is still suitable for case. the common In reality, hooks and render props shake hands and play well together. But if you must decide between either of the two, though, let’s put that decision on stress. Want to see the end benchmark? Skip to the , otherwise read on… comparison If your are not familiar with hooks and the render props pattern - don’t worry - a good starting point is Render Props , Use a Render Prop! and Hooks at a Glance . A list of resources is also available at the end. Render Props are Not Dead with that name by was the trigger for this writing. It outlines how we got from HoCs to hooks. Watch it, it should make things clearer. A talk Erik Rasmussen I remember the voice in my head hitting the play button on that React Europe video: . At that time was released and I was happy with it. An immediate v3 rewrite would destroy my positive feeling. “Wait, should I do another rewrite of my library, getting rid of the render props I so much like” v2 of Enform May be you: work with hooks, but may not fully understand them see hooks as a magic solution want to rewrite it all with hooks If so, then what follows may be a surprise. The Problem Hooks and render props can solve the same problem. The question is which one does a better job? When? Does it matter to bother with wrapper components and render props since we already have the hooks api? It is conceptually about moving state away from your components, so that it is reusable. To answer, let’s work with the common form example below throughout this journey. It’s trivial and you have probably seen it many times: class Form extends Component { constructor(props) { super(props); this.state = { name: props.name || "", email: props.email || "" } } render() { return ( { this.setState({ name: e.target.value }); }} /> { this.setState({ email: e.target.value }); }} /> ); } } < > form < = = => input value {this.state.name} onChange {e < = = => input value {this.state.email} onChange {e </ > form The form is intentionally kept simpler. The snippet may force you to think: . Right, and state is the primary suspect. Adding to that, usually you have more fields involved in the form and need to handle validation, submission, API calls, error messages too. Of course, as a result “This is a recipe for disaster” your component will grow and you may need to relieve the state logic by abstracting it somehow. Handling State Abstraction with Hooks Look at this simplified code: { [name, setName] = useState( ); [email, setEmail] = useState( ); ( <h1>This is a simple form!</h1> <form> <input value={name} onChange={e => { setName(e.target.value); }} /> <input value={email} onChange={e => { setEmail(e.target.value); }} /> </form> </> ); } ( ) function Form const "" const "" return <> Try it out in codesandbox. It is the same form component, but using a function instead of a class and the hook. Simple move that already made things nicer. Including more fields to this controlled form is as easy as handling more state in the component. useState [name, setName] = useState( ); [email, setEmail] = useState( ); [phone, setPhone] = useState( ); [address, setAddress] = useState( ); ... const "" const "" const "" const "" Using hooks and functional components is already a win. OK, but you bump into another trouble - component state is growing together with the form. From that point there are two options. Create a separate form component or a custom hook to hold the state heavy lifting. Form Custom Hook I assume you know how to build one. There are many examples out there, so let’s not focus on the useForm implementation below. What is interesting is how it improves our component and how it gets consumed. Remember we are slowly getting to the pain point - would custom hook be the best approach here. Lastly, please excuse once again the simplicity as the idea is just to illustrate the pattern. { { values, setValue } = useForm(); ( <h1>This is a simple form!</h1> <form> <input value={values.name} onChange={e => { setValue("name", e.target.value); }} /> <input value={values.email} onChange={e => { setValue("email", e.target.value); }} /> </form> </> ); } ( ) function Form const return <> contains all the details. This codesandbox Ideally adding more logic would result in just the (the render) part growing, while manages the state for you. jsx useForm Side note: useForm() (it’s a pretty common name) may miss-reference you to react-hook-form . The name matches, but the idea is different. react-hook-form is not solving the state problem described here, but avoiding it by having the form as uncontrolled instead. Getting back to our example. Adding errors and submit features: { { values, setValue, errors, submit } = useForm(); ( <h1>This is a simple form!</h1> <form onSubmit={submit}> <input value={values.name} onChange={e => { setValue("name", e.target.value); }} /> <input value={values.email} onChange={e => { setValue("email", e.target.value); }} /> <input value={values.phone} onChange={e => { setValue("phone", e.target.value); }} /> <p>{errors.phone}</p> </form> </> ); } ( ) function Form const return <> Still, it scales pretty good. You can move more logic into the hook and make it reusable for all form components in your project. The state no longer resides in , but the component will continue to react on field changes. At the end, it is the same usage, but moved in . <Form /> useState useForm The obvious benefits of this approach are that it’s intuitive (no weird syntax), scales pretty well and it’s probably part of the React future. Ok, but how about render props? Handling State with Render Prop Component Unloading the Form component state-wise using the render props approach requires you to create a wrapper component. So, no hooks on the surface, but a regular component. In this example it is children that serves as a render prop, but you may use render (or something else) instead. { ( <h1>This is a simple form!</h1> <FormManager> {({ values, setValue }) => ( <form> <input value={values.name} onChange={e => { setValue("name", e.target.value); }} /> <input value={values.email} onChange={e => { setValue("email", e.target.value); }} /> </form> )} </FormManager> </> ); } ( ) function Form return <> Curious about FormManager's implementation? Here is the . codesandbox Abstracting the state away in a weird way, right? Yes, this is how it is. From the official docs: The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function. - exactly what seems awkward when you see render props for the first time. Other than that it works similar to except is just a normal component. This pattern might be familiar, especially if you are working on third party libraries or using such. “…using a prop whose value is a function” useForm <FormManager /> Why is that? The render props approach has similar benefits to hooks, but looks strange and sometimes doesn’t scale efficiently. Imagine the following: { ( <div ref={innerRef}> <DragDropContext onDragEnd={handleDragEnd}> {() => ( <Droppable> {() => ( <Draggable> {provided => ( <div ref={provided.innerRef} {...provided} /> )} </Draggable> )} </Droppable> )} </DragDropContext> </div> )} </Swipeable> ( ) function MyComponent return {innerRef => ( < = = > Swipeable onSwipeLeft {handleSwipeLeft} onSwipeRight {handleSwipeRight} ); } This snippet is actually taken from . production code Nested wrapper components with render props. Oh, that doesn’t look very promising. It may even trick some people to believe the pattern is obsolete in favor of . Hooks , that’s true. “do everything with hooks” don’t suffer the nesting issue But if render props had no pros over hooks the article is leading to a dead end. It’s not, I promise. There is something else, though, which is not about the syntax. Keep on… Reality Check Let’s recap. Remember this part from the beginning? <> <form> ... < > This is a simple form! < > h1 </ > h1 /form> </ I intentionally left more elements ( ) than just the in the jsx. It is supposed to serve as a hint, because . Often they render others or third party ones which you don’t have control over. <h1 /> <form /> in reality some components aren’t that simple A more realistic example would look like so: { { values, setValue } = useForm(); ( <Header /> <Navigation /> <SomeOtherThirdPartyComponent /> <form> <input value={values.name} onChange={e => { setValue("name", e.target.value); }} /> <input value={values.email} onChange={e => { setValue("email", e.target.value); }} /> </form> <Footer /> </> ); } ( ) function Page const return <> Now, I know you may say: . Yes and you would be right - seems the correct thing to do, but not always. who uses jsx like that? You can obviously extract the form logic into another component and render it here instead There are three general restrictions with hooks: you need react@16.8.0 (the one with hooks) you have to use functional components you may fall into re-render issues Skipping the first two… If you have class components and a lower version of react you can’t use hooks obviously. The third one, however, is the cornerstone when deciding between hooks and render props. You May Fall into Re-render Issues Given the last example, every time you type in the form fields will be called causing the whole component to re-render. And because you are updating the state, this is expected. But not desirable. setValue <Page /> Suddenly filling a form may become a very expensive operation. React is clever enough to protect you from unnecessary renders, but it won’t go against its principles. Every component has its own catch-ups and you need to work around these, so it’s safe against renders. Unfortunately, it may not be the case with , and because, let’s imagine, you don’t have time to refactor them. And with you may even not be able to do so. <Header /> <Navigation /> <Footer /> <SomeOtherThirdPartyComponent /> Not many options here. . As a consequence - you will need to in your project, forcing the tree to grow inevitably. Extracting the from in a separate component is the way to go with hooks repeat that for every form What if you are building a form library that exports a hook like ? Do you prefer your users to do the extra extraction step above? Not a big deal you may say. Not a big one, but a less flexible one. useForm Hooks are not remedy for all problems and they are not intended to serve that purpose. The hypothetic (or not) primer above is one of these cases where you may need the extra flexibility. Stick to hooks, but add some sugar. Re-render only What Is Relevant Render props don’t suffer the same re-render issue hooks do. Here is why. { ( <Header /> <Navigation /> <SomeOtherThirdPartyComponent /> <FormManager> {({ values, setValue }) => ( <form> <input value={values.name} onChange={e => { setValue("name", e.target.value); }} /> <input value={values.email} onChange={e => { setValue("email", e.target.value); }} /> </form> )} </FormManager> <Footer /> </> ); } ( ) function Page return <> is immune to unnecessary renders. You can add up more jsx with no side effects. <FormManager /> ensures whatever change is made in the form it will be isolated in that form. <Page /> Of course you can always break the useful pattern. Imagine updating some related state as a result of form manipulation. It will result in additional renders. But then, it won’t be FormManager’s fault. <Page /> Now if your form library exports component with a render prop instead, its users get that extra flexibility. They are no longer forced to create additional components. Comparison in Action Putting these two implementations side by side: Feel free to play with . the set up Voilà. You can now see the render outcome of each form. The one on the left (custom hook) is causing re-renders in all Page children, while the one on the right (render prop) doesn’t. Final Words Render props are very useful if you want to isolate part of the jsx and inject some state without introducing side effects to your components. It is very common that many render prop implementations are using hooks internally so saying would be extreme. Hooks support the pattern pretty well and gain the extra flexibility they lack in some situations. This is to consider when deciding between one OR the other approach. “it’s hooks or nothing” But hey, your form library can also export both the wrapper component and the hook. This too is very common. That makes working on open source so fun. 📩 for my next handcrafted Javascript/React writing. Issued monthly. You can also for more frequent updates. Subscribe follow me on Twitter Resources The list here is not extensive. Some of the topics are more advanced, some are touching just the basics. You are welcome to add to it. (React docs) Render Props (React docs) Hooks at a Glance (Michael Jackson) Use a Render Prop! (Jared Palmer) Awesome React Render Props (Erik Rasmussen) Render Props are Not Dead Credits: Photo by Austin Neill on Unsplash. Previously published at https://webup.org/blog/render-props-vs-hooks/ ⚛