7

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 Answers 3

4

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
Sign up to request clarification or add additional context in comments.

4 Comments

This is exactly what should be done. Just because you can write a component as an ES6 class doesn't mean that all components should be classes.
I am supporting your solution. I did no know that you can do not have to use PropTypes.shape for props in component.
Avoid answering questions with: "The best way to do X is like ABC, therefore here is ABC" - He wanted an ES6 version of the pure function version, NOT a repeat of the exact problem he was referring to.
Shouldn't it be const Todo = ... (or function Todo(props) { ... }) instead of function Todo = ...?
4

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.

1 Comment

The OP asked for an ES6 class, your answer is a syntax error.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.