React PowerPlug is a set of pluggable renderless components and helpers that provides different types of state and logics so you can use with your dumb components. It creates a state and pass down the logic to the children, so you can handle your data. Read about Render Props pattern.
- 👌 Dependency free
- 🔌 Plug and play
- 🔮 Tree shaking friendly (ESM, no side effects)
- 📦 Super tiny (~3kb)
- 📚 Well documented
- 🍻 Bunch of awesome utilities
See quick examples
import { State, Toggle } from 'react-powerplug'
import { Pagination, Tabs, Checkbox } from './MyDumbComponents'
<State initial={{ offset: 0, limit: 10, totalCount: 200 }}>
{({ state, setState }) => (
<Pagination {...state} onChange={(offset) => setState({ offset })} />
)}
</State>
<Toggle initial={true}>
{({ on, toggle }) => (
<Checkbox checked={on} onChange={toggle} />
)}
</Toggle>
// You can also use a `render` prop instead
<Toggle
initial={false}
render={({ on, toggle }) => (
<Checkbox checked={on} onChange={toggle} />
)}
/>
This branch is unstable and is in active development.
For the latest stable version go to 0.1-stable branch
Note This is a kind of a cheat sheet for fast search.
If you want a more detailed API Reference and examples for each component see full docs
Component | Component Props | Render Props | |
---|---|---|---|
<State> | { initial, onChange } |
{ state, setState } |
👇 📚 |
<Toggle> | { initial, onChange } |
{ on, toggle, set } |
👇 📚 |
<Counter> | { initial, onChange } |
{ count, inc, dec, incBy, decBy, set } |
👇 📚 |
<Value> | { initial, onChange } |
{ value, setValue, set } |
👇 📚 |
<Map> | { initial, onChange } |
{ set, get, over, values } |
👇 📚 |
<Set> | { initial, onChange } |
{ values, add, clear, remove, has } |
👇 📚 |
<List> | { initial, onChange } |
{ list, first, last, push, pull, sort, set } |
👇 📚 |
<Hover> | { onChange } |
{ isHovered, bind } |
👇 📚 |
<Active> | { onChange } |
{ isActive, bind } |
👇 📚 |
<Focus> | { onChange } |
{ isFocused, bind } |
👇 📚 |
<Touch> | { onChange } |
{ isTouched, bind } |
👇 📚 |
<FocusManager> | { onChange } |
{ isFocused, blur, bind } |
👇 📚 |
<Input> | { initial, onChange } |
{ set, value, bind } |
👇 📚 |
<Form> | { initial, onChange } |
{ input, values } |
👇 📚 |
<Interval> | { delay } |
{ stop, start, toggle } |
👇 📚 |
<Compose> | { components } |
depends on components prop | 👇 📚 |
Name | |
---|---|
compose(...components) | 📚 |
composeEvents(...objOfEvents) | 📚 |
<State initial={{ isLoading: false, data: null }}>
{({ state, setState }) => (
<DataReceiver
data={state.data}
onStart={() => setState({ isLoading: true })}
onFinish={data => setState({ data, isLoading: false })}
/>
)}
</State>
<Toggle initial={true}>
{({ on, toggle }) => <Checkbox checked={on} onChange={toggle} />}
</Toggle>
<Counter initial={0}>
{({ count, inc, dec }) => (
<CartItem
productName="Lorem ipsum"
unitPrice={19.9}
count={count}
onAdd={inc}
onRemove={dec}
/>
)}
</Counter>
<Value initial="React">
{({ value, setValue }) => (
<Select
label="Choose one"
options={['React', 'Angular', 'Vue']}
value={value}
onChange={setValue}
/>
)}
</Value>
<Map initial={{ sounds: true, graphics: 'medium' }}>
{({ set, get }) => (
<Settings>
<ToggleCheck checked={get('sounds')} onChange={c => set('sounds', c)}>
Game Sounds
</ToggleCheck>
<Select
label="Graphics"
options={['low', 'medium', 'high']}
selected={get('graphics')}
onSelect={value => set('graphics', value)}
/>
</Settings>
)}
</Map>
<Set initial={['react', 'babel']}>
{({ values, remove, add }) => (
<TagManager>
<FormInput onSubmit={add} />
{values.map(tag => (
<Tag onRemove={() => remove(tag)}>{tag}</Tag>
)}
</TagManager>
)}
</Set>
<List initial={['Buy new shoes']}>
{({ list, pull, push }) => (
<Todo>
<TodoFormInput onSubmit={push} />
{list.map(todo => (
<TodoItem onDelete={() => pull(i => i === todo)}>
{todo}
</TodoItem>
)}
</Todo>
)}
</List>
<Hover>
{({ isHovered, bind }) => (
<div {...bind}>
You are {isHovered ? 'hovering' : 'not hovering'} this div.
</div>
)}
</Hover>
<Active>
{({ isActive, bind }) => (
<div {...bind}>
You are {isActive ? 'clicking' : 'not clicking'} this div.
</div>
)}
</Active>
<Touch>
{({ isTouched, bind }) => (
<div {...bind}>
You are {isTouched ? 'touching' : 'not touching'} this div.
</div>
)}
</Touch>
<Focus>
{({ isFocused, bind }) => (
<div>
<input {...bind} placeholder="Focus me" />
<div>You are {isFocused ? 'focusing' : 'not focusing'} input.</div>
</div>
)}
</Focus>
<Input initial="hello world">
{({ bind, value }) => (
<div>
<ControlledInput {...bind} />
<div>You typed {value}</div>
</div>
)}
</Input>
<Form initial={{ subject: '', message: '' }}>
{({ input, values }) => (
<form
onSubmit={e => {
e.preventDefault()
console.log(values)
}}
>
<ControlledInput placeholder="Subject" {...input('subject').bind} />
<ControlledTextArea placeholder="Message" {...input('message').bind} />
<Submit>Send</Submit>
</form>
)}
</Form>
<Interval delay={1000}>
{({ stop, start }) => (
<>
<div>The time is now {new Date().toLocaleTimeString()}</div>
<button onClick={() => stop()}>Stop interval</button>
<button onClick={() => start()}>Start interval</button>
</>
)}
</Interval>
If you want to avoid 'render props hell' you can compose two or more components in a single one.
📚 For complete guide, see docs
import { Compose } from 'react-powerplug'
<Compose components={[Toggle, Counter]}>
{(toggle, counter) => (/* ... */)}
</Compose>
import { compose } from 'react-powerplug'
const ToggleCounter = compose(
<Counter initial={5} />,
<Toggle initial={false} />
)
<ToggleCounter>
{(toggle, counter) => (
<ProductCard {...} />
)}
</ToggleCounter>
yarn add react-powerplug
npm i react-powerplug
<script src="https://unpkg.com/react-powerplug/dist/react-powerplug.min.js"></script>
exposed as ReactPowerPlug
Thanks goes to these wonderful people (emoji key):
Renato Ribeiro 💻 🎨 📖 |
Bogdan Chadkin 💻 📖 |
Travis Arnold 💻 📖 🐛 |
Max Graey 💻 |
Mateusz Burzyński 🐛 |
Andy Edwards 💻 |
Andrea Vanini 🐛 |
---|---|---|---|---|---|---|
Ivan Starkov 🐛 |
Sean Roberts 📖 |
This project follows the all-contributors specification. Contributions of any kind welcome!
You can help improving this project sending PRs and helping with issues.
Also you can ping me at Twitter