Skip to content

Instantly share code, notes, and snippets.

@jordanrios94
Last active January 31, 2019 11:38
Show Gist options
  • Save jordanrios94/a5efc8416ef38eb6c53dc3ddae4b4079 to your computer and use it in GitHub Desktop.
Save jordanrios94/a5efc8416ef38eb6c53dc3ddae4b4079 to your computer and use it in GitHub Desktop.
Controlled Components

Controlled components

Allow their state to be driven from props, as seen below.

<input
  placeholder="email"
  onChange={e =>
    this.setState({
      email: e.target.value
    })
  }
  value={this.state.email}
/>

Why do we set the value prop here? Can the input work without it?

It can work, we can type in it, but we can't control its internal state. For example, we can't clear the value.

class App extends Component {
  state = {email: ''};
  render() {
    return (
      <input
        placeholder="email"
        onChange={e =>
          this.setState({
            email: e.target.value
          })
        }
      />
    );
  }
}

Now reading the following code below, we have control over the component's value.

class App extends Component {
  state = {email: ''};
  clearValue = () => {
    this.setState({email: ''});
  };
  render() {
    return (
      <div>
        <input
          placeholder="email"
          onChange={e =>
            this.setState({
              email: e.target.value
            })
          }
          value={this.state.email}
        />
        <button onClick={this.clearValue}> clear </button>
      </div>
    );
  }
}

So uh, let's make another Tabs component

class App extends React.Component {
  render() {
    return (
      <Tabs
        tabs={[
          {id: 'home', label: 'Home'},
          {id: 'contact', label: 'Contact'},
          {id: 'about', label: 'About'}
        ]}
      />
    );
  }
}

And another Tab component (still dumb)

const Tab = ({label, active, onSelect}) => (
  <div
    style={{
      backgroundColor: active ? 'purple' : 'white'
    }}
    onClick={onSelect}
  >
    {label}
  </div>
);

But what if we wanted to control the state of the Tabs component from the App component?

First, we can add a value prop. This prop will tell the Tabs component that we want to control the current value from the outside.

<Tabs
  value="contact"
  tabs={[
    {id: 'home', label: 'Home'},
    {id: 'contact', label: 'Contact'},
    {id: 'about', label: 'About'}
  ]}
/>

Then we need to add an onChange prop to the Tabs component.

class App extends React.Component {
  state = {selectedTab: 'contact'};
  selectTab = tab => this.setState({selectedTab: tab.id});
  render() {
    return (
      <Tabs
        onChange={tab => this.selectTab(tab)}
        value={this.state.selectedTab}
        tabs={[
          {id: 'home', label: 'Home'},
          {id: 'contact', label: 'Contact'},
          {id: 'about', label: 'About'}
        ]}
      />
    );
  }
}
class Tabs extends React.Component {
  state = {
    selectedTab:
      this.props.value || this.props.defaultValue || this.props.tabs[0].id
  };
  selectTab = tab => {
    const {onChange} = this.props;
    let isControlled = onChange && typeof onChange === 'function';
    
    isControlled ? onChange(tab) : this.setState({selectedTab: tab.id});
  };
  render() {
    const {tabs, value} = this.props;
    return tabs.map(tab => (
      <Tab
        id={tab.id}
        label={tab.label}
        onSelect={() => this.selectTab(tab)}
        active={tab.id === (value || this.state.selectedTab)}
      />
    ));
  }
}

Controlled (value comes from outside)

<Tabs
  onChange={tab => this.setState({selectedTab: tab})}
  value={this.state.value}
  tabs={[
    {id: 'home', label: 'Home'},
    {id: 'contact', label: 'Contact'},
    {id: 'about', label: 'About'}
  ]}
/>

Uncontrolled (internal state)

<Tabs
  tabs={[
    {id: 'home', label: 'Home'},
    {id: 'contact', label: 'Contact'},
    {id: 'about', label: 'About'}
  ]}
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment