Legally Binding

Posted by fakebenjay on April 10, 2017

One of the most frustrating aspects of React.js is the regular requirement that one bind() methods in order for them to function. JavaScript was written in a week in the early 1990s, and the bind() step in a moden framework like React really highlights the language’s haphazard origins.

According to Mozilla’s JS docs:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

In short, bind() is a neat, but tricky way of adjusting a function’s scope by redefining the value of its ‘this.’ It is incredibly useful in the right context, and totally esoteric in a vacuum, especially for JS n00bs.

A good example of the necessity of bind() in React can be found in the component below, which controls and defines individual pets in a pet adoption web app.

class Pet extends React.Component {
  constructor() {
    super();
    this.handleAdopt = this.handleAdopt.bind(this)
  }

  genderIcon() {
    if(this.props.pet.gender === "male") {
      return '♂'
    } else if (this.props.pet.gender === "female") {
      return '♀'
    }
  }

  handleAdopt() {
    this.props.onAdoptPet(this.props.pet.id)
  }

  adoptionButton() {
    if (this.props.isAdopted) {
      return <button className="ui disabled button" disabled>Already adopted</button>
    } else {
      return <button onClick={this.handleAdopt} className="ui primary button">Adopt pet</button>
    }
  }

  render() {
    let pet = this.props.pet

    return (
      <div className="card">
        <div className="content">
          <a className="header">{pet.name} ({this.genderIcon()})</a>
          <div className="meta">
            <span className="date">Type: {pet.type}</span>
          </div>
          <div className="description">
            <p>Age: {pet.age}</p>
            <p>Weight: {pet.weight} lbs</p>
          </div>
        </div>
        <div className="extra content">
          { this.adoptionButton() }
        </div>
      </div>
    );
  }
}

To understand why our Pet component has three methods, but only one bind(), it’s important to look at handleAdopt()’s scope without that bind().

▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
    ▓▓▓▓▓▓▓▓
  ▓

  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓
    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
      ▓▓▓▓▓▓▓▓▓▓
    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
      ▓▓▓▓▓▓▓▓▓▓
    ▓
  ▓

  handleAdopt() {
    this.props.onAdoptPet(this.props.pet.id)
  }

  adoptionButton() {
    if (this.props.isAdopted) {
      return <button className="ui disabled button" disabled>Already adopted</button>
    } else {
      return <button onClick={this.handleAdopt} className="ui primary button">Adopt pet</button>
    }
  }

  ▓▓▓▓▓▓▓▓▓▓
    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓

    ▓▓▓▓▓▓▓▓
      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
        ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
            ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
          ▓▓▓▓▓▓
          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
            ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
            ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
          ▓▓▓▓▓▓
        ▓▓▓▓▓▓
        ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
          { this.adoptionButton() }
        ▓▓▓▓▓▓
      ▓▓▓▓▓▓
    ▓▓
  ▓
▓

handleAdpot() is called by the adoptionButton() method, which is referenced at the bottom of the page. When referenced, it’s trying to reference either its own props, which do not exist, because it is a function, and not an entire React component. If this webapp were a car, as it is right now, handleAdopt() would be referencing the wheels of the very act of driving a car (which is a very weird sentence because how the hell would that work???), as opposed to the car’s wheels.

But adding in that bind() in the constructor…

this.handleAdopt = this.handleAdopt.bind(this)

…where the final “this” that’s being bound refers to the entire JS class, expands the scope to encompass the entire component, props and events and all.

Easy enough, right? But what if you don’t want to bind the whole class, but just a few specific values here and there?

To do this, we’ll need to employ a trick called “currying…”

#1 on the list of things I’d rather do than yell at JavaScript: eat at this place

In this example, the render function of the component below (which controls and defines the table in a dynamic spreadsheet app) displays a certain number of rows on the page, which should change value when they’re typed into, and should know when they’re selected and de-selected (or focused/blurred)…

export default class Table extends React.Component {

  render () {
    return(
      <table className="table" onBlur={this.props.onBlur}>
        <tbody>
          {this.props.table.map((row, rowIndex) => {
            return <Row key={rowIndex}
                        onChange={this.props.onChange.bind(null, rowIndex)}
                        onFocus={this.props.onFocus.bind(null, rowIndex)}
                        row={row}
                        />
          })
          }
        </tbody>
      </table>
    )
  }
}

…weird. There’s no onChange() or onFocus() method anywhere in this class? What gives? The answer is up one level in the Spreadsheet component, which calls upon the table:

export default class Spreadsheet extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      focused: null,
      table: [['']]
    };

    this.handleAddColumn = actions.addColumn.bind(this)
    this.handleAddRow = actions.addRow.bind(this)
    this.handleRemoveColumn = actions.removeColumn.bind(this)
    this.handleRemoveRow = actions.removeRow.bind(this)
    this.handleChange = actions.changeCell.bind(this)
    this.handleBlur = actions.blurCell.bind(this)
    this.handleFocus = actions.focusCell.bind(this)
  }

  render () {
    return (
      <div className='spreadsheet'>
        <h1>My little spreadsheet</h1>

        <div className='spreadsheet__buttons'>
          <button onClick={this.handleAddColumn}>Add Column</button>
          {' '}
          <button onClick={this.handleAddRow}>Add Row</button>
          {' '}
          <button onClick={this.handleRemoveColumn}>Remove Column</button>
          {' '}
          <button onClick={this.handleRemoveRow}>Remove Row</button>
        </div>

        <Table
          table={this.state.table}
          onChange={this.handleChange}
          onFocus={this.handleFocus}
          onBlur={this.handleBlur}/>

        <div className='spreadsheet__focused'>
          {
            this.state.focused
            ? <strong>{this.state.focused[0]} — {this.state.focused[1]}</strong>
            : null
          }
        </div>
      </div>
    );
  }
}

Yup, there are the bound functions being passed into “Table”’s props, but where are the original functions? Those are defined in a separate file and a separate “actions” folder (another cool, but unrelated feature of React), and called here (hence “actions.functionName” instead of “this.functionName”.) Let’s have a look at actions.changeCell().

function changeCell (rowIndex, columnIndex, event) {
  let newTable = this.state.table
  newTable[rowIndex][columnIndex] = event.target.value
  this.setState({
    table: newTable
  })
}

What this function does is change the displayed value of a cell at a certain row index and column index when a certain event is executed (in this case, typing into it.) By the time we’re mapping the Rows, the function doesn’t need access to the entire Table class, it just needs to know what row it’ll be adding to (defining the column happens one level below this, but that’s unimportant for now.) Doing that requires just binding in the passed in rowIndex, and the columnIndex (which since that’s being defined separately, should be passed in as null, for now)

Got it yet?