React

Composition with React.cloneElement

4 min read

React.cloneElement is named appropriately. You use it to bestow additional props on a React element object. These are typically created by JSX. Given these objects actually have a stable API, it seems like a superfluous function.

Here, we are cloning the div (a React element has a .type and .props; an element’s props includes its children), and adding the className prop.

Text

Did you spot the mistake? This snippet may work, but it will subtly break when you use either of the two reserved props: key and ref. See for yourself:

Entered value:

(none)

When entering anything into the textarea, value is not updated, because textRef is no longer connected to the manually cloned textarea.

This is the main reason we have cloneElement. It maintains the original element’s .ref and .key properties, which are not stored inside its props, but instead have their own field on the element object. If you override ref or key with the second (config) argument, those will still be used instead.

Entered value:

(none)

By using cloneElement here, the ref is maintained. The same applies to keys.

cloneElement is not a commonly-used function and you’re likely to be met by confusion for wanting to use it. It can be hard for a newcomer to understand what it does. If you use it to inject required props, TypeScript won’t understand that, and will complain that those required props are missing.

If you want to use it to make an API more ergonomic, consider only using it for optional props, like className.

Cloning may seem expensive, but it’s actually quite cheap. There isn’t much overhead compared to just having more JSX elements. In the eyes of React, you are already re-creating the entire element tree with createElement calls. This is why its diffing algorithm is quite complex already. With cloneElement, you are simply modifying a createElement object before passing it to React.

Together with the React.Children methods, you can interact with the children prop. React provides these methods as an abstraction in order to be able to change the underlying data structure used by children. You can always use React.Children.toArray if you need to use array methods or perform random access.

Wrapping children in an element

Read section Wrapping children in an element

Rather than having to provide elementType and elementProps props to pass to createElement inside Elementify, you can use the cloneElement pattern to provide an element which serves as a template.

Why

How

Buy Now

The second argument is used to add additional props to a cloned element. null means to not add any additional props. All arguments after the second are converted to an array of children.

Disclaimer here adds a child to each of the children provided to it.

You're missing out! Invest in CFDs now!Investing involves risk; you may lose money.
You're missing out! Bitcoin is going to the moon!Investing involves risk; you may lose money.

You could also prepend children this way, or add them anywhere in the middle (or conditionally) with toArray.

Injecting props into a child

Read section Injecting props into a child

In this example, Form provides a large abstraction around the form fields specified as children by ManagedInputs. Submit the form to see its data.