Skip to content

Instantly share code, notes, and snippets.

@m3g4p0p
Created November 6, 2017 17:55
Show Gist options
  • Save m3g4p0p/a1dc51fa720746b093a77282816813f1 to your computer and use it in GitHub Desktop.
Save m3g4p0p/a1dc51fa720746b093a77282816813f1 to your computer and use it in GitHub Desktop.
Bind functions to instances in a way that maintains strict equality when done multiple times
const bindings = new WeakMap()
const getFns = context => {
if (!bindings.has(context)) {
bindings.set(context, {})
}
return bindings.get(context)
}
const bindOnce = (fn, context) => {
const fns = getFns(context)
const key = fn.toString()
if (!bindings[key]) {
bindings[key] = fn.bind(context)
}
return bindings[key]
}
export default bindOnce
@m3g4p0p
Copy link
Author

m3g4p0p commented Nov 6, 2017

Why?

In React, it's bad to declare functions inside a render() method and pass them down as props, as pure components just check for equality of the props received, and two sepearate function are never equal even if they do the same thing on the same instance.

A common solution to this problem is to do bind the methods to this in the constructor, which is of course a bit of a hassle. Also you can't conveniently write arrow functions inline in the render mathod, which would be more readable in some cases.

So another possible solution would be a custom bind function as this one, which associates functions with instances in a weak map, and looks up existing bindings by their stringified versions.

Usage

class MyComponent extends Component {
  render () {
    return (
      <MyPureComponent
        onClick={bindOnce(() => {
          console.log(this.state.something)
        }, this)}
      />
    )
  }
}

Inspired by reflective-bind:
https://github.com/lencioni/reflective-bind
https://flexport.engineering/ending-the-debate-on-inline-functions-in-react-8c03fabd144

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment