A chantastic guide to components

This is a component

function AComponent() {
return <>Hello components</>
}

A component can compose other components

function AComponent() {
return <h1>Hello components</h1>
}

A component can me bade composeable via the children prop

function AComponent({children = 'Hello components'}) {
return <h1>{children}</h1>
}

This is another component

function App() {
return <main>Hello app</main>
}

One component can render another

function App() {
return (
<main>
<AComponent>Hello app</AComponent>
</main>
)
}

Options can be passed to components (these are called props)

function App() {
return (
<main>
<AComponent style={color: "red"}>Hello app!</AComponent>
</main>
)
}

But props must be accepted and implemented

function AComponent({
style = null,
children = 'Hello components',
}) {
return <h1 style={style}>{children}</h1>
}

props can have default values

function AComponent({
style = {textDecoration: 'underline'},
children = 'Hello components',
}) {
return <h1 style={style}>{children}</h1>
}

but these values are not automatically merged with incoming values

merging values must be done manually as well

let defaultStyles = {textDecoration: 'underline'}
function AComponent({
style = null,
children = 'Hello components',
}) {
let mergedStyles = {...defaultStyles, ...style}
return <h1 style={mergedStyles}>{children}</h1>
}

Control of merging can be delegated via prop callback functions — allowing component consumers to modify defaults

let defaultStyles = {textDecoration: 'underline'}
function AComponent({
style = null,
children = 'Hello components',
}) {
let mergedStyles =
typeof style === 'function'
? style(defaultStyles)
: {...defaultStyles, ...style}
return <h1 style={mergedStyles}>{children}</h1>
}
function App() {
return (
<main>
<AComponent
style={(defaultStyles) => ({
...defaultStyles,
...{color: 'red'},
})}
>
Hello app!
</AComponent>
</main>
)
}

Default resolvers, and formatters can be exported for use by component consumers

let defaultStyles = {textDecoration: 'underline'}
export function resolveDefaultStyles(incomingStyle) {
return typeof incomingStyle === 'function'
? incomingStyle(defaultStyles)
: {...defaultStyles, ...incomingStyle}
}
export function AComponent({
style = null,
children = 'Hello components',
}) {
let mergedStyles = resolveDefaultStyles(style)
return <h1 style={mergedStyles}>{children}</h1>
}

to avoid manually clearlisting all valid HTML attributes, use rest and spread syntax for props

export function AComponent({
style = null,
children = 'Hello components',
...restProps
}) {
let mergedStyles = resolveDefaultStyles(style)
return (
<h1 style={mergedStyles} {...restProps}>
{children}
</h1>
)
}

This implementation makes it simple to opt out of defaults (where necessary)

function App() {
return (
<main>
<AComponent
style={
(/* ignore defaults */) => {
color: 'red'
}
}
>
Hello app!
</AComponent>
</main>
)
}

children can be an array

function AComponent({
chant = false,
children = 'Hello components',
}) {
return <h1>{[children, children, children].join(' ')}</h1>
}

children can be many types.

<AComponent>A string</AComponent>
<AComponent>{123}</AComponent>
<AComponent>{boolean}</AComponent>
<AComponent>[1, 2, 3]</AComponent>
<AComponent>[1, <>2</>, 3]</AComponent>
<AComponent><>stuff</></AComponent>
<AComponent><h1>stuff</h1></AComponent>

So treat children as opaque

React.children