Higher Order Component that helps manage renderable slots
Why
When developing React components, you probably want to design them to be reusable. Sometimes, it could be really convenient to create placeholders in your component, and let others fill them with content of their choice. React already gives you the option to pass children
and later on use them in your render method, which is not very scalable. At some point, you might want to have multiple "children
" props and this is where react-placeholders
comes in handy.
Of course, you can just pass rendered JSX or strings via props, but as your team grows and your project becomes more complex, those props will become a mess. Consider just a few aspects of it:
- Are you going to call a prop
header
,headerSlot
,headerBlock
? Or mayberenderHeader
? - Is it going to be a callback or rendered JSX?
- Should you treat it as a string?
Obviously, you'll need some standardization around the way you structure those props. So, the reason react-placeholders
was created is to help your team unify the way you do "renderable props."
Terminology
In order to avoid confusion, let's agree on the following terms:
- Slot - a component prop used to fill a placeholder
- Renderer - value that will show up in a slot
- Callback Renderer - function that takes necessary parameters and renders content
Installation
npm install --save react-placeholders
or
yarn add react-placeholders
Usage
Import
ES6+
import withSlots from 'react-placeholders';
CommonJS
const withSlots = require('react-placeholders');
Decorator
import React, { Component } from 'react';
import withSlots from 'react-placeholders';
@withSlots(['header', 'footer', 'body'])
export default class Popup extends Component {
render() {
const { header, body, footer } = this.props;
return (
<div>
<header>{header()}</header>
<section>{body()}</section>
<footer>{footer({ isShown: true })}</footer>
</div>
);
}
}
Without Decorator
import React, { Component } from 'react';
import withSlots from 'react-placeholders';
class Popup extends Component {
render() {
const { header, body, footer } = this.props;
return (
<div>
<header>{header()}</header>
<section>{body()}</section>
<footer>{footer({ isShown: true })}</footer>
</div>
);
}
}
export default withSlots(['header', 'footer', 'body'])(Popup);
Rendering a Component with Slots
<Popup
headerSlot="Greetings"
bodySlot={<p>Hello, world!</p>}
footerSlot={() => (<span>Copyright {new Date().getFullYear()}</span>)}
/>
ref
Retrieving <Popup
ref={(ref) => { /* reference to the wrapper */ }}
innerRef={(ref) => { /* reference to the instance of Popup */ }}
/>