Spreading Remaining Props
Spreading props isn't always the right choice, but in many cases it is very helpful. After you've destructured the props you need for your component, it's wise to add a ...remaining
prop that you will then spread onto the most important DOM element inside of your component. It could be the outer div
or maybe the input
for a form element. The important part is that you spread (...
) the remaining
props onto a single element so you can pass down things like:
aria-*
attributesdata-*
attributesclassName
prop (important for CSS-in-JS)title
attribute- All of the input attributes for form fields
Why is this important?
When choosing to spread props you give up a certain level of control in exchange for authoring speed and a reduction of maintenance points over time. Here's a very quick example:
function MyChildComponent(props) {const { message } = propsreturn <div>{message}</div>}function MyParentComponent() {return <MyChildComponent message="Hello World!" />}
This is all fine and good until one day you want to pass a className
down to the child component. Like any reasonable person, you add a className
prop and forward it dutifully, like so:
function MyChildComponent(props) {const { message, className } = propsreturn (<div className={ className }>{ message }</div>}function MyParentComponent() {return (<MyChildComponentmessage="Hello World!"className="u-color--red"/>)}
Another day goes by and you need a title
attribute, so you repeat the process:
function MyChildComponent(props) {const { message, className, title } = propsreturn (<div className={className} title={title}>{message}</div>)}function MyParentComponent() {return (<MyChildComponentmessage="Hello World!"className="u-color--red"title="and good day to you as well!"/>)}
On this goes with a few more props, all naturally supported by the DOM. Perhaps you need a testid
for the fantastic react-testing-library
, and some aria-*
attributes.
function MyChildComponent(props) {const {message,className,title,"data-testid": dataTestId,"aria-hidden": ariaHidden,} = propsreturn (<divclassName={className}title={title}data-testid={dataTestId}aria-hidden={ariaHidden}>Hello World!</div>)}function MyParentComponent() {return (<MyChildComponentmessage="Hello World!"className="u-color--red"title="and good day to you as well!"data-testid="my-child-component"aria-hidden="true"/>)}
Each time you want to pass down a new attribute for the outer div
you have to write both the prop and the code to forward the prop. Obnoxious. Let's take a look at how destructuring ...remaining
props – using the rest
parameter (...
) – and spreading them out on the outer div
– using the spread
operator (...
) – can reduce the code and, more importantly, the number of times you need to modify this implementation.
function MyChildComponent(props) {const { message, ...remaining } = propsreturn (<div {...remaining}>{message}</div>}function MyParentComponent() {return (<MyChildComponentmessage="Hello World!"className="u-color--red"title="and good day to you as well!"data-testid="my-child-component"aria-hidden="true"/>)}
We get the same outcome, but now we get to treat the child component just like a primitive HTML element. Neat!