Contents
- Stateless function
- JSX spread attributes
- Destructuring arguments
- Conditional rendering
- Children types
- Array as children
- Function as children
- Render callback
- Children pass-through
- Proxy component
- Style component
- Event switch
- Layout component
- Container component
- Higher-order component
- State hoisting
- Controlled input
Stateless function
Stateless functions are a brilliant way to define highly reusable components. They don't hold state
; they're just functions.
const Greeting = () => <div>Hi there!</div>
They get passed props
and context
.
const Greeting = (props, context) =>
<div style={{color: context.color}}>Hi {props.name}!</div>
They can define local variables, where a function block is used.
const Greeting = (props, context) => {
const style = {
fontWeight: "bold",
color: context.color,
}
return <div style={style}>{props.name}</div>
}
But you could get the same result by using other functions.
const getStyle = context => ({
fontWeight: "bold",
color: context.color,
})
const Greeting = (props, context) =>
<div style={getStyle(context)}>{props.name}</div>
They can have defined defaultProps
, propTypes
and contextTypes
.
Greeting.propTypes = {
name: PropTypes.string.isRequired
}
Greeting.defaultProps = {
name: "Guest"
}
Greeting.contextTypes = {
color: PropTypes.string
}
JSX spread attributes
Spread Attributes is a JSX feature. It's syntactic sugar for passing all of an object's properties as JSX attributes.
These two examples are equivalent.
// props written as attributes
<main className="main" role="main">{children}</main>
// props "spread" from object
<main {...{className: "main", role: "main", children}} />
Use this to forward props
to underlying components.
const FancyDiv = props =>
<div className="fancy" {...props} />
Now, I can expect FancyDiv
to add the attributes it's concerned with as well as those it's not.
<FancyDiv data-id="my-fancy-div">So Fancy</FancyDiv>
// output: <div class="fancy" data-id="my-fancy-div">So Fancy</div>
Keep in mind that order matters. If props.className
is defined, it'll clobber the className
defined by FancyDiv
<FancyDiv className="my-fancy-div" />
// output: <div className="my-fancy-div"></div>
We can make FancyDiv
s className always "win" by placing it after the spread props ({...props})
.
// my `className` clobbers your `className`
const FancyDiv = props =>
<div {...props} className="fancy" />
You should handle these types of props gracefully. In this case, I'll merge the author's props.className
with the className
needed to style my component.
const FancyDiv = ({ className, ...props }) =>
<div
className={["fancy", className].join(' ')}
{...props}
/>
destructuring arguments
Destructuring assignment is an ES2015 feature. It pairs nicely with props
in Stateless Functions.
These examples are equivalent.
const Greeting = props => <div>Hi {props.name}!</div>
const Greeting = ({ name }) => <div>Hi {name}!</div>
The rest parameter syntax (...
) allows you to collect all the remaining properties in a new object.
const Greeting = ({ name, ...props }) =>
<div>Hi {name}!</div>
In turn, this object can use JSX Spread Attributes to forward props
to the composed component.
const Greeting = ({ name, ...props }) =>
<div {...props}>Hi {name}!</div>
Avoid forwarding non-DOM props
to composed components. Destructuring makes this very easy because you can create a new props
object without component-specific props
.
conditional rendering
You can't use regular if/else conditions inside a component definition. The conditional (ternary) operator is your friend.
if
{condition && <span>Rendered when `truthy`</span> }
unless
{condition