Advanced React Component Patterns

by,

Shakthishree

Simple Toggle Component

import React, {Component} from 'react';
import Switch from './switch';

export default class Toggle extends Component {
  state = {on: false};

  toggle = () => {
    this.setState(({on}) => ({on: !on}));
    this.props.onToggle(!this.state.on);
  };

  render() {
    return (
     <div>
      <Switch 
        on={this.state.on} 
        onClick={this.toggle} 
      />
      <div>
      {this.state.on ? 'on' : 'off'}
      </div>
     </div>
    );
  }
}

import React, {Component} from 'react';
import Toggle from './toggle';

export default class User extends Component {
  onToggle = ( on ) => {
   console.log( on ? 'On' : 'Off' );
  };

  render() {
   return (
    <Toggle onToggle = { this.onToggle } />
   )
  }
}

Flexible Toggle Component

Compound Component

export default class Toggle extends Component {
  state = {on: false};

  static On = ({on}) => 
    (on ? <div>On</div> : null);
  static Off = ({on}) => 
    (on ? null : <div>Off</div>);
  static Button = ({on, toggle}) => 
    <Switch on={on} onClick={toggle} />;

  toggle = () => {
    this.setState(({on}) => ({on: !on}));
    this.props.onToggle(!this.state.on);
  };

  render() {
   return (
     React.Children.map(
       this.props.children, child =>
         React.cloneElement(child, {
           on: this.state.on,
           toggle: this.toggle,
       });
     );
    );
  }
}

export default class User extends Component {
 onToggle = on => {
  console.log(on ? 'on' : 'false' );
 }; 

 render() {
  return (
   <Toggle onToggle={this.onToggle}>
     <Toggle.Button />
     <Toggle.Off />
     <Toggle.On />
   </Toggle>
  );
 }
}

Compound Component

export default class Toggle extends Component {
  state = {on: false};

  static On = ({on, children}) => 
    (on ? children : null);
  static Off = ({on, children}) => 
    (on ? null : children);
  static Button = ({on, toggle}) => 
    <Switch on={on} onClick={toggle} />;

  toggle = () => {
    this.setState(({on}) => ({on: !on}));
    this.props.onToggle(!this.state.on);
  };

  render() {
   return (
     React.Children.map(
       this.props.children, child =>
         React.cloneElement(child, {
           on: this.state.on,
           toggle: this.toggle,
       });
     );
    );
  }
}

export default class User extends Component {
 onToggle = on => {
  console.log(on ? 'on' : 'false' );
 }; 

 render() {
  return (
   <Toggle onToggle={this.onToggle}>
       <Toggle.Button />
       <Toggle.Off>
           <div>Off</div>
       </Toggle.Off>
       <Toggle.On>
           <div>On</div>
       </Toggle.On>
   </Toggle>
  );
 }
}

Compound Component

React Context API


const ToggleContext = React.createContext();

static On = ({children}) => 
 <ToggleContext.Consumer>
  {({on, children}) => {
     return on ? children : null;}}
 </ToggleContext.Consumer>;
   
static Off = ({children}) =>
 <ToggleContext.Consumer>
  {({on, children}) => {
     return on ? null : children;}}
 </ToggleContext.Consumer>;

static Button = ({children}) =>
 <ToggleContext.Consumer>
  {({on, toggle}) => 
      <Switch on={on} onClick={toggle} />}
 </ToggleContext.Consumer>;

render() {
 return (
  <ToggleContext.Provider 
   value={{on: this.state.on, 
      toggle: this.toggle}}>
     {this.props.children}
  </ToggleContext.Provider>);}


export default class User extends Component {
 onToggle = on => {
  console.log(on ? 'on' : 'false' );
 }; 

 render() {
  return (
   <Toggle onToggle={this.onToggle}>
     <div  style={{
                height: '40px',
                width: '60px',
                backgroundColor: '#7eb36d',
                marginTop: '10px',
                border: '1px solid grey',
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
              }}>
      <Toggle.Button />
     </div>
     <Toggle.Off />
     <Toggle.On />
   </Toggle>
  );
 }
}
export default class Toggle extends Component {
  state = {on: false};

  static On = ({on, children}) => 
    (on ? children : null);
  static Off = ({on, children}) => 
    (on ? null : children);
  static Button = ({on, toggle}) => 
    <Switch on={on} onClick={toggle} />;

  toggle = () => {
    this.setState(({on}) => ({on: !on}));
    this.props.onToggle(!this.state.on);
  };

  render() {
   return (
     React.Children.map(
       this.props.children, child =>
         React.cloneElement(child, {
           on: this.state.on,
           toggle: this.toggle,
       });
     );
    );
  }
}

React Context API

function ToggleConsumer(props) {
 return (
  <ToggleContext.Consumer {...props}>
   {context => {
     if (!context) {
      throw new Error(
        `Compound components are used
         outside provider`)}
     return props.children(context)
   }}
  </ToggleContext.Consumer>
  )}

 static On = ({children}) => (
    <ToggleConsumer>
      {({on}) => (on ? children : null)}
    </ToggleConsumer>)

 static Off = ({children}) => (
    <ToggleConsumer>
      {({on}) => (on ? null : children)}
    </ToggleConsumer>)

 static Button = props => (
   <ToggleConsumer>
    {({on, toggle}) => (
<Switch  on={on} onClick={toggle} {...props} />)}
   </ToggleConsumer>)

export default class User extends Component {
 onToggle = on => {
  console.log(on ? 'on' : 'false' );
 }; 

 render() {
  return (
   <div onToggle={this.onToggle}>
     <div  style={{
                height: '40px',
                width: '60px',
                backgroundColor: '#7eb36d',
                marginTop: '10px',
                border: '1px solid grey',
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
              }}>
      <Toggle.Button />
     </div>
     <Toggle.Off />
     <Toggle.On />
   </div>
  );
 }
}

const ToggleContext = React.createContext();

static On = ({children}) => 
 <ToggleContext.Consumer>
  {({on, children}) => {
     return on ? children : null;}}
 </ToggleContext.Consumer>;
   
static Off = ({children}) =>
 <ToggleContext.Consumer>
  {({on, children}) => {
     return on ? null : children;}}
 </ToggleContext.Consumer>;

static Button = ({children}) =>
 <ToggleContext.Consumer>
  {({on, toggle}) => 
      <Switch on={on} onClick={toggle} />}
 </ToggleContext.Consumer>;

render() {
 return (
  <ToggleContext.Provider 
   value={{on: this.state.on, 
      toggle: this.toggle}}>
     {this.props.children}
  </ToggleContext.Provider>);}


HOC

function wrapper(Component, props) {
  return (
    <ToggleContext.Consumer {...props}>
      {context => {
        return <Component context={context} />;
      }}
    </ToggleContext.Consumer>
  );
}

static On = ({children}) =>
       wrapper(({on}) => (on ? children : null));
static Off = ({children}) => 
       wrapper(({on}) => (on ? null : children));
static Button = ({children}) =>
    wrapper(context => {
      return <Switch 
            on={context.on} 
            onClick={context.context.toggle} />;
    });

render() {
    return (
      <ToggleContext.Provider
        value={{on: this.state.on, 
        toggle: this.toggle}}
        {...this.props}
      />
);

render() {
 return (
      <Toggle onToggle={this.onToggle}>
        <Toggle.Button />
        <Toggle.Off>
          <div>The button is off</div>
        </Toggle.Off>
        <Toggle.On>
          <div>The button is on</div>
        </Toggle.On>
      </Toggle>
)
}
const ToggleContext = React.createContext();

static On = ({children}) => 
 <ToggleContext.Consumer>
  {({on, children}) => {
     return on ? children : null;}}
 </ToggleContext.Consumer>;
   
static Off = ({children}) =>
 <ToggleContext.Consumer>
  {({on, children}) => {
     return on ? null : children;}}
 </ToggleContext.Consumer>;

static Button = ({children}) =>
 <ToggleContext.Consumer>
  {({on, toggle}) => 
      <Switch on={on} onClick={toggle} />}
 </ToggleContext.Consumer>;

render() {
 return (
  <ToggleContext.Provider 
   value={{on: this.state.on, 
      toggle: this.toggle}}>
     {this.props.children}
  </ToggleContext.Provider>);}


Use case

HOC to handle state

import React,{Component, Fragment} from 'react';
import Toggle from './toggle';

class User extends Component{ 
render() {
    return(
     <Fragment>
      <div>
       {this.props.state.on ? 'On' : 'Off' }
      </div>
      <Switch
        onReset={this.onReset}
        on={this.props.state.on}
        onClick={this.props.toggle}
       />
      </Fragment>);
  }
}

export default Toggle(User)

import React, {Component} from 'react';

function ToggleHOC ( Comp ){
  return class Toggle extends Component {
    state = {on: false};
    toggle = () => {
      this.setState({on: !this.state.on});
    };
    render() {
      return <Comp {...this.props} 
                   state={this.state} 
                   toggle={this.toggle} />;
    }
  };
};

export default ToggleHOC;

Render Props

import React, {Component} from 'react';
import Switch from './switch';

class Toggle extends Component {
 state = {on: false};

 toggle = () => {
    this.setState({on: !this.state.on});
    this.props.onToggle(this.state.on);
  };

 
 render(){
  return(
   this.props.children({
     on: this.state.on,
     toggle: this.toggle,
   })
  )
 }
}

import React, {Component} from 'react';
import './switch';

export default class User extends Component {
 onToggle = on => {
  console.log(on ? 'on' : 'false' );
 }; 

 render() {
  return (
    <Toggle
      onToggle={this.onToggle}
    >
      {({on, toggle, reset}) =>
        <div>
          {on ? 'on' : 'off'}
        </div>
        <Switch on={on} onClick={toggle} />
      }
    </Toggle>
  );
 }
}

Control Props

import React, {Component} from 'react';
import Switch from './switch';

export default class Toggle extends Component {

  render() {
    return (
     <div>
      <Switch 
        on={this.props.on} 
        onClick={this.props.onToggle} 
      />
      <div>
      {this.props.on ? 'on' : 'off'}
      </div>
     </div>
    );
  }
}

import React, {Component} from 'react';
import Toggle from './Toggle';

export default class User extends Component {
 state = {on: false};

 onToggle(){
   this.setState({on: !this.state.on});
 }
 
 render(){
   return(
     <Toggle 
        on={this.state.on} 
        onToggle={this.onToggle.bind(this)};
   )
 }

State Initializers

import React, {Component} from 'react';
import Switch from './switch';

class Toggle extends Component {
 static defaultProps = {
    initialOn: false,
 }

 initialState = {on: this.props.initialOn ? 
            this.props.initialOn : 
            this.props.defaultProps.initialOn};

 state = this.initialState;

 toggle = () => {
    this.setState({on: !this.state.on});
    this.props.onToggle(this.state.on)
  };

 
 render(){
  return(
   this.props.children({
     on: this.state.on,
     toggle: this.toggle
   })
  )
 }
}

import React, {Component} from 'react';
import './switch';

export default class User extends Component {
 onToggle = on => {
  console.log(on ? 'on' : 'false' );
 }; 

 render() {
  return (
    <Toggle
      initialOn= {true}
      onToggle={this.onToggle}
    >
      {({on, toggle, reset}) =>
        <div>
          {on ? 'on' : 'off'}
        </div>
        <Switch on={on} onClick={toggle} />
      }
    </Toggle>
  );
 }
}

Use Case

State Reducers

state = {on : false}
internalSetState(changes, callback) {
 this.setState(state => {
  const changesObject =
   typeof changes === 'function' ?
     changes(state) : 
     changes;
  const reducedChanges =
  this.props.stateReducer(state, changesObject) 
   || {}
  return Object.keys(reducedChanges).length
   ? reducedChanges
   : null
  }, callback)
}
  
toggle = () => {
    this.internalSetState(
      ({on}) => ({on: !on}),
      () => this.props.onToggle(this.state.on),
    )
}

render(){
  return(
   this.props.children({
     on: this.state.on,
     toggle: this.toggle,
   })
  )
}



state = {timesClicked: 0}

toggleStateReducer = (state, changes) => {
   return this.state.timesClicked >= 4 ? 
        {...changes, on: false}
      : changes;
} 

render() {
 return (
  <Toggle
   stateReducer={this.toggleStateReducer}
   initialOn= {true}
   onToggle={this.onToggle}
  >
   {({on, toggle}) =>
    <div> {on ? 'on' : 'off'} </div>
    <Switch on={on} onClick={toggle} />
    <div>
     {this.state.timesClicked > 4 ?
      <div> Whoa, you clicked too much! </div> 
      :
      <div>
       Click count:{this.state.timesClicked}
      </div> 
     }
    </div>
   }
  </Toggle>
Made with Slides.com