7
votes

I'm new to ES6. Got a bit confused on different ways to write a React component. I started with a "React.createClass" then moved to "extends React.Component" with ES6 classes syntax.

Following Redux tutorial now I see they define components in this way

import React, { PropTypes } from 'react'

const Todo = ({ onClick, completed, text }) => (
    <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} >
        {text}
    </li>
)

Todo.propTypes = {
    onClick: PropTypes.func.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
}

export default Todo

How can I refactor this "function" moving to a ES6 class which extends React.component? I guess the return JSX is the render() method, isn't it? What about onClick, completed, text arguments?

Thank you

3

3 Answers

4
votes

For your component is actually best to make it a pure function, rather than an ES6 class, because it can render as a function of its props and does not maintain state. You can still use ES6 syntax though (exporting, arrow functions, etc.).

Facebook's explanation: "In an ideal world, most of your components would be stateless functions because in the future we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. This is the recommended pattern, when possible."

import { PropTypes } from 'react'

function Todo = (props) => (
    <li onClick={props.onClick} style={{ textDecoration: props.completed ? 'line-through' : 'none' }} >
        {props.text}
    </li>
)

Todo.propTypes = {
    onClick: PropTypes.func.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
}

export default Todo
4
votes

The ES6 syntax equivalent would be:

import React, { Component, PropTypes } from 'react'

class Todo extends Component {
  render() {
    const { onClick, completed, text } = this.props

    return (
      <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} >
          {text}
      </li>
    )
  }
}
Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  completed: PropTypes.bool.isRequired,
  text: PropTypes.string.isRequired
}

export default Todo

If you are using babel to transpile your code and you have the class properties transform then you can do the following:

import React, { Component, PropTypes } from 'react'

class Todo extends Component {
  static propTypes = {
    onClick: PropTypes.func.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
  }

  render() {
    const { onClick, completed, text } = this.props

    return (
      <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} >
          {text}
      </li>
    )
  }
}

export default Todo

Just to be clear this second example cannot be considered "standard ES6", however I find the static properties so much cleaner so I thought it was worth showing this approach too.

0
votes

Moving beyond ES6 into TypeScript, for React version 17 and above you can do:

import type { FC } from "react";

interface TodoProps {
  onClick: () => void;
  hasCompleted: () => boolean;
  text: string;
}

const Todo: FC<TodoProps> = ({ onClick, hasCompleted, text }) => (
  <li
    onClick={onClick}
    style={{ textDecoration: hasCompleted ? "line-through" : "none" }}
  >
    {text}
  </li>
);

export default Todo;

If you don't want implicit children you can drop the import type line and rewrite like:

const Todo = ({ onClick, hasCompleted, text }: TodoProps) => (

If you do, however, you'll also need to add children to the type declaration and spend time trying to figure out how it should be typed—not necessary if using FC as children are assumed.

For more info on declaring Functions in TypeScript visit More on Functions in the TypeScript docs.