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.
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 key
s.
A word of advice
Read section A word of advicecloneElement
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.
Use cases
Read section Use casesTogether 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 elementRather 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.
Appending children
Read section Appending childrenDisclaimer
here adds a child to each of the children provided to it.
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 childIn this example, Form
provides a large abstraction around the form fields specified as children by ManagedInputs
. Submit the form to see its data.