react-powerplug

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.

Highlights

  • :ok_hand: Dependency free
  • :electric_plug: Plug and play
  • :crystal_ball: Tree shaking friendly (ESM, no side effects)
  • :package: Super tiny (~3kb)
  • :books: Well documented
  • :beers: 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} />
  )}
/>

⚠️ Master is unstable

This branch is unstable and is in active development.
For the latest stable version go to 0.1-stable branch

Components

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 CONTAINERS
<State> { initial, onChange } { state, setState } :point_down: :books:
<Toggle> { initial, onChange } { on, toggle, set } :point_down: :books:
<Counter> { initial, onChange } { count, inc, dec, incBy, decBy, set } :point_down: :books:
<Value> { initial, onChange } { value, set } :point_down: :books:
<Map> { initial, onChange } { set, get, over, values } :point_down: :books:
<Set> { initial, onChange } { values, add, clear, remove, has } :point_down: :books:
<List> { initial, onChange } { list, first, last, push, pull, sort, set } :point_down: :books:
FEEDBACK CONTAINERS
<Hover> { onChange } { hovered, bind } :point_down: :books:
<Active> { onChange } { active, bind } :point_down: :books:
<Focus> { onChange } { focused, bind } :point_down: :books:
<Touch> { onChange } { touched, bind } :point_down: :books:
<FocusManager> { onChange } { focused, blur, bind } :point_down: :books:
FORM CONTAINERS
<Input> { initial, onChange } { set, value, bind } :point_down: :books:
<Form> { initial, onChange } { input, values } :point_down: :books:
OTHER
<Interval> { delay } { stop, start, toggle } :point_down: :books:
<Compose> { components } depends on components prop :point_down: :books:

Utilities

Name
compose(...components) :books:
composeEvents(...objOfEvents) :books:

Examples

State

<State initial={{ loading: false, data: null }}>
  {({ state, setState }) => (
    <DataReceiver
      data={state.data}
      onStart={() => setState({ loading: true })}
      onFinish={data => setState({ data, loading: false })}
    />
  )}
</State>

Toggle

<Toggle initial={true}>
  {({ on, toggle }) => <Checkbox checked={on} onChange={toggle} />}
</Toggle>

Counter

<Counter initial={0}>
  {({ count, inc, dec }) => (
    <CartItem
      productName="Lorem ipsum"
      unitPrice={19.9}
      count={count}
      onAdd={inc}
      onRemove={dec}
    />
  )}
</Counter>

Value

<Value initial="React">
  {({ value, set }) => (
    <Select
      label="Choose one"
      options={['React', 'Angular', 'Vue']}
      value={value}
      onChange={set}
    />
  )}
</Value>

Map

<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

<Set initial={['react', 'babel']}>
  {({ values, remove, add }) => (
    <TagManager>
      <FormInput onSubmit={add} />
      {values.map(tag => (
        <Tag onRemove={() => remove(tag)}>{tag}</Tag>
      ))}
    </TagManager>
  )}
</Set>

List

<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

<Hover>
  {({ hovered, bind }) => (
    <div {...bind}>
      You are {hovered ? 'hovering' : 'not hovering'} this div.
    </div>
  )}
</Hover>

Active

<Active>
  {({ active, bind }) => (
    <div {...bind}>
      You are {active ? 'clicking' : 'not clicking'} this div.
    </div>
  )}
</Active>

Touch

<Touch>
  {({ touched, bind }) => (
    <div {...bind}>
      You are {touched ? 'touching' : 'not touching'} this div.
    </div>
  )}
</Touch>

Focus

<Focus>
  {({ focused, bind }) => (
    <div>
      <input {...bind} placeholder="Focus me" />
      <div>You are {focused ? 'focusing' : 'not focusing'} input.</div>
    </div>
  )}
</Focus>

Input

<Input initial="hello world">
  {({ bind, value }) => (
    <div>
      <ControlledInput {...bind} />
      <div>You typed {value}</div>
    </div>
  )}
</Input>

Form

<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

<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>

Composing Components

If you want to avoid 'render props hell' you can compose two or more components in a single one.
:books: 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>

Watch 'Rapid Prototyping with React PowerPlug' by Andrew Del Prete on egghead.io


Install

Node Module

yarn add react-powerplug
npm i react-powerplug

UMD

<script src="https://unpkg.com/react-powerplug/dist/react-powerplug.min.js"></script>

exposed as ReactPowerPlug

GitHub