• Stars
    star
    256
  • Rank 159,219 (Top 4 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 10 years ago
  • Updated about 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Easily create controllable components

react-controllables

Easily create controllable components

If you've worked with forms in ReactJS, you're probably familiar with the idea of controlled and uncontrolled components. Put simply, controlled components have their state controlled by another component whereas uncontrolled components manage their own state. It turns out that this idea can be really useful for custom components too.

Use Case

Imagine you've designed a TabBar component. When you click on a tab, it becomes selected and the other tabs in the bar become deselected. The selected tab is stored as state in the component. (As a pleasant side-effect, this makes it nice and easy to demo in isolation.)

Everything is fine until one day when the designer of your site decides to add another component to the page that also changes the selected tab. Now you've got a problem: you need to hoist the state to a higher level so it can be shared between the two components.

Instead of ripping out the state management from your TabBar component (which would make it impossible to play with the component on a page by itself), make it controllable.

How

  1. Write a "dumb" component that doesn't manage its state at all but accepts one or more props and corresponding onPROPNAMEChange callbacks.
  2. Use controllable to create a higher-order component from the dumb one.

Note: There's one exception to the onPROPNAMEChange convention: if the prop name is "value," the callback will be simply "onChange". This is done to match the conventions in React itself.

Note: react-controllables used to be implemented with a mixin and have a different (more complicated!) usage. The mixin is still included at react-controllables/mixin (or Controllables.Mixin in the standalone build) but deprecated. Switch!

Example

We'll use our TabBar example and represent the selection as an integer using the selectedTabIndex prop.

class TabBar extends React.Component {

  render() {
    var selectedTabIndex = this.props.selectedTabIndex;
    return (
      <ul onClick={ this.handleClick.bind(this) }>
        <li className={ selectedTabIndex == 0 ? 'selected' : '' }>Tab Zero!</li>
        <li className={ selectedTabIndex == 1 ? 'selected' : '' }>Tab One!</li>
        <li className={ selectedTabIndex == 2 ? 'selected' : '' }>Tab Two!</li>
      </ul>
    );
  }

  handleClick(event) {
    // Call the `onSelectedTabIndexChange` callback with the new value.
    if (!this.props.onSelectedTabIndexChange) return;
    var el = event.target;
    var index = Array.prototype.indexOf.call(el.parentNode.children, el);
    this.props.onSelectedTabIndexChange(index);
  }

}

TabBar.defaultProps = {selectedTabIndex: 0};

TabBar.propTypes = {
  selectedTabIndex: PropTypes.number.isRequired,
  onSelectedTabIndexChange: PropTypes.func,
};

Next, use the controllable util to create a higher-order component, telling it which props you want to be managed.

import controllable from 'react-controllables';
TabBar = controllable(TabBar, ['selectedTabIndex']);

We now have a TabBar component that can store its own state OR be controlled! Just use it like normal:

<TabBar />

We can specify a value for it to start with using defaultPROPNAME:

<TabBar defaultSelectedTabIndex={ 2 } />

Or we can make it a controlled component and manage the state at a higher level:

<TabBar
  selectedTabIndex={ indexFromSomewhereElse }
  onSelectedTabIndexChange={ handler } />

Unlike React inputs, components built with react-controllables can't really be said to be either "controlled" or "uncontrolled" generally. That's because a single component can have both controlled and uncontrolled values. For example, consider this variation of our TabBar:

TabBar = controllable(TabBar, ['selectedTabIndex', 'color']);

We could have both "selectedTabIndex" and "color" be controlled:

<TabBar selectedTabIndex={ 2 } color="blue" />

Or neither:

<TabBar defaultSelectedTabIndex={ 2 } />

(Remember, default values don't make a component controlled, they just set the initial internal state.)

Or only one!

<TabBar color="blue" />

Decorator Support

The react-controllables API is also designed to work with JavaScript decorator proposal. Decorators provide a very elegant way to use react-controllables (and higher-order components in general) if you're using a transpiler that supports them, like Babel 5.0 or greater:

@controllable(['selectedTabIndex', 'color'])
class TabBar extends React.Component {

  // [SNIP] Body same as above.

}

More Repositories

1

django-imagekit

Automated image processing for Django. Currently v4.0
Python
2,260
star
2

python-markdownify

Convert HTML to Markdown
Python
1,036
star
3

pilkit

Utilities and processors built for, and on top of PIL
Python
195
star
4

monorouter

An isomorphic JS router
JavaScript
141
star
5

react-mediaswitch

Choose your DOM based on media queries
CoffeeScript
59
star
6

react-frozenhead

Make your whole page a React component and render it on the server
JavaScript
57
star
7

django-classbasedsettings

Class based settings. You couldn't get that from the title?
Python
52
star
8

httpplease.js

The polite HTTP request library for node and the browser
JavaScript
31
star
9

django-html5boilerplate

A packaging of Paul Irish's HTML5 Boilerplate for Django projects.
Python
20
star
10

markdown-with-front-matter-loader

A webpack loader for markdown with yaml front matter (think Jekyll)
JavaScript
19
star
11

jquery-icbiacontrol

Style browser controls without losing their native behaviors.
JavaScript
12
star
12

assert-this.js

A clear assertion style that uses virtual methods instead of wrappers
JavaScript
7
star
13

grunt-jinja

A grunt plugin for compiling Jinja2 templates with James Long's nunjucks templating system
CoffeeScript
6
star
14

jquery.picplus

Take control of your images.
JavaScript
6
star
15

django-throttleandcache

Cache the results of arbitrary function calls!
Python
5
star
16

gulp-heroku-deploy-slug

Deploy slug archives to Heroku
JavaScript
4
star
17

react-template

Organized rendering.
JavaScript
3
star
18

wrapperator.js

Create a function that can be used as a wrapper or method decorator
JavaScript
2
star
19

jquery.picplus.queueup

Use a load queue to load your picplus pictures. Load the important stuff first.
JavaScript
2
star
20

jquery.picplus.lazyload

Add lazyloading to your picplus pictures. Load them when they're scrolled into your viewport.
CoffeeScript
2
star
21

httpplease-promises

A plugin that adds promise support to httpplease
JavaScript
2
star
22

monorouter-react

ReactJS tools for monorouter
JavaScript
2
star
23

django-maid

Django maid cleans up your orphaned media files.
Python
1
star
24

Google-Closure.tmbundle

The fastest way to get started with Google Closure.
JavaScript
1
star
25

jquery.parallaxin

Create an element that scrolls at a different speed than the document but always stays within a container.
JavaScript
1
star
26

django-scspostgis

A PostGIS db backend that works with standard_conforming_strings.
Python
1
star