DEV Community

JavaScript Joel
JavaScript Joel

Posted on

Creating React components without this. #nothis

Change

Easily create React components that are completely devoid of this.

You might remember me as the psychopath who wrote Rethinking JavaScript: The complete elimination and eradication of JavaScript's this

Well, I'm back and this time I've got a new React Component for you!

But WHY, you ask angrily?

I subscribe to the Douglas Crockford way of thinking.

"There are a few (features) we know that are going to be bad. The worst is Class. Class was the most requested new feature in JavaScript. And all of the requests came from Java programmers who have to program in JavaScript and don't want to learn how to do that. So they wanted something that looks like Java so they could be more comfortable. Those people will go to their graves not knowing how misserable they are." - Douglas Crockford

And in the same way that Douglas Crockford found his programs improved when he stopped using this, I found this to be true in my code as well.

"I was very surprised to discover that my programs got better. It was not a hardship to not use this. It was actually a benefit. My programs got smaller and easier and you know, that's what we're all looking for" -- Douglas Crockford

Even so, I do understand that there is little chance of me changing your mind because...

"Programmers are as emotional and irrational as normal people. So when the solution finally arrives, most of us will reject it." -- Douglas Crockford

This isn't just some anecdotal statement. Crockford goes on to provide real world examples, such as...

"It took a generation to agree that GOTO was a bad idea. We argued passionately, emotionally for 2 decades about whether we should use GOTO or not." -- Douglas Crockford

Well, this is the GOTO of JavaScript and I understand that it's not going to go away overnight. But I would like to hope that it won't take us two decades like GOTO.

Doctor is hurts when i .this

What does this all mean for React Components?

React does have a functional component, but React's most popular way of creating components is to create a class and extend from React.Component and a Class comes with this.

Then I asked myself, what if I could use React.Component but without this. And that is how NoThis.Component was born.

You'll notice NoThis.Component is used in a way familiar to React.Component. The context you would previously access via this is available as the first function argument.

import React from 'react'
import NoThis from 'nothis-react'

class Counter extends NoThis.Component {
  state = { count: 0 }

  increment(ctx) {
    ctx.setState(state => ({ count: state.count + 1 }))
  }

  render(ctx) {
    return (
      <div>
        <button onClick={ctx.increment}>{ctx.state.count}</button>
      </div>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

increment and render have become pure functions, acting only on their inputs!

Argument Destructuring

If you love destructuring as much as I do, then code like this now becomes possible!

import React from 'react'
import NoThis from 'nothis-react'

class Counter extends NoThis.Component {
  state = { count: 0 }

  increment({ setState }) {
    setState(({ count }) => ({ count: count + 1 }))
  }

  render({ increment, state: { count } }) {
    return (
      <div>
        <button onClick={increment}>{count}</button>
      </div>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

Now that is some sexy code.

Excluding Functions

A function can be excluded from nothis by writing it as a class property.

class Counter extends NoThis.Component {
  increment = () => {
    this.setState(({ count }) => ({ count: count + 1 }))
  }
}
Enter fullscreen mode Exit fullscreen mode

Wrapup

This is some experimental future code, so treat it as such. Please do play with it and report any bugs or request features at the Github repo.

Join the #nothis movement and hit me up on the tweety.

If you hated this article, you might also hate these too:

#nothis

Cheers!

Top comments (9)

Collapse
 
imichael profile image
✨iMichael✨ • Edited

Fat arrow functions make old complaints about "this" moot, especially in React. Instead of

 increment(ctx) {
    ctx.setState(state => ({ count: state.count + 1 }))
  }

simply do:

 increment = () => {
    this.setState({count: this.state.count + 1 })
 }

Benefits:

  • It's idiomatic.
  • It looks great.
  • You don't have to bind this or reason about what "this" is at all.
  • If you also write Python or Ruby, your mental model can be this == self.

#thisIsFine

Collapse
 
joelnet profile image
JavaScript Joel

This is absolutely a fantastic solution and it definitely solves one of this's problems. This is a perfect example of that.

I still prefer to eliminate this completely. I don't want to have to ask if the function has been written correctly so that this behaves the way I expect it.

I don't have to worry about "it's okay to use here, this way, but not over here." It's easier for me to program without it.

It's much like null. An even simpler concept than this. We know to put nullchecks all over.

// check that null
if (x) {
  x.y()
}

// shortcut check
x && x.y()

But no matter how hard we try, we are still destined to run into NullReferenceException.

And much like Douglas Crockford says...

"I was very surprised to discover that my programs got better. It was not a hardship to not use this. It was actually a benefit. My programs got smaller and easier and you know, that's what we're all looking for" -- Douglas Crockford

... banning this in your programs, much like null won't be a hardship. It will become a benefit, making your programs smaller and easier!

The only way to eliminate 100% of this related bugs, is to eliminate this.

Cheers!

Collapse
 
josefjelinek profile image
Josef Jelinek • Edited

Unfortunately, this code is not idiomatic as you should pass a function to the setState in this case or else from several increments in a row most get ignored as the state is updated asynchronously and they will have a stale state... nothis FTW is what I take feom this comment in the end...

Collapse
 
kepta profile image
Kushan Joshi

How do I add properties dynamically to this, taking an example from React docs on refs.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // Focus the text input using the raw DOM API
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // autofocus the input on mount
    this.focusTextInput();
  }

  render() {
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

If you say use ctx to dynamically attach things, that would just be no better than that = this and defeat the whole point you are making.

Collapse
 
brainrape profile image
Márton Boros

increment is not pure since it mutates non-local state.

Collapse
 
ben profile image
Ben Halpern

I was writing React when I authored this tweet. I know it’s a controversial subject but I’m right there with you.

Collapse
 
joelnet profile image
JavaScript Joel

Haha! I don't know whether I should laugh or cry :)

Collapse
 
romanholovin profile image
Roman Holovin

recompose + @hocs/with-lifecycle provides more smooth solution than this one :)

Collapse
 
joelnet profile image
JavaScript Joel

I will totally agree with you on this. These both provide a very elegant functional design and if I start a project from scratch, I'll probably go with one of these. Sometimes the the code style is out of our control and not all developers are up to speed with FP.

The intent of <NoThis.Component /> is not to push an FP paradigm. It's only purpose is to promote this to a function argument.

I wouldn't say recompose is a better solution than @hocs/with-lifecycle or vice versa. They both have their place. At the same time I feel like this library also has it's place.

Cheers!