Skip to content

Type of Components

sergio edited this page Jan 30, 2021 · 9 revisions

index

The main take away here is the difference between class and functional components. Class components are versatile and fully featured components. They can be anything we want them to be. Functional components exchange the class component's bells and whistles for simplicity and a small performance boost.

Regular Component

import React, { Component } from 'react'

class App extends Component {
  render() {
    return <div></div>
  }
}

export default App

Pure Component

Does an automatic comparison of old and new props and state therefore not having shouldComponentUpdate.

import React, { PureComponent } from 'react'

class App extends PureComponent {
  render() {
    return <div></div>
  }
}

export default App

Functional Component

Although React is clever when it comes to rendering class components, every class component, when rendered, goes through a series of checks related to its lifecycle. If we do not need to use state or lifecycle methods, we can avoid these checks by writing a functional component.

import React, { PureComponent } from 'react'

class App extends PureComponent {
  render() {
    return <div></div>
  }
}

export default App

// simpler form:
import React from 'react'

const App = props => <div>{props.greeting}</div>

export default App

for instance we can use it for a reusable Button component that has a consistent style but receives props that define its text and click event

import React from 'react'

const Button = ({ handleClick, text })=> <button style="myButton" onClick={ handleClick }>{ text }</button>

export default Button

notice the technique to use {key} consist in the objects passed into the function is deconstructed as this {key1} = {key1: 1, key2: 2} //=> 1

These are not different types of components, but instead, are a way of thinking on how to organize a React app.

Presentational components are only concerned with displaying content. They typically don't deal with state, or have a lot of added logic within them. They receive props and display content. The Button component from the functional component section above is a great example of this.

In React, we can compartmentalise - each piece can be a component (NavLinks, Menu, Search, etc..) and since they all go together, we can create a parent component, that acts as a container for everything:

import React, { Component } from 'react'
import Logo from './Logo'
import NavLinks from './NavLinks'
import DropMenu from './DropMenu'
import Search from './Search'

class NavigationContainer extends Component {

  state = {
    query: "",
    username: ""
  }

  render() {
    // <>...</> is a a React fragment - it does not render anything to the DOM, but can wrap multiple JSX elements
    return (
      <>
        <Logo />
        <NavLinks />
        <DropMenu username={ this.state.username }/>
        <Search query= {this.state.query } handleChange={ this.handleChange } handleSubmit={ this.handleSubmit }/>
      </>
    )
  }

  handleSubmit = event => { ... }
  handleChange = event => { ... }
}

Using this sort of set up, none of the imported components need to have their own state, nor do they need to have any functions defined. Container components, like NavigationContainer, deal with managing state and class methods.

Keeping all the more complex logic in one place makes it easier to follow the flow of information. It also keeps many components simpler and free of clutter.

Container components, having to deal with state, are usually class components. Presentational components are most often functional components as they don't need to contain custom methods, relying mainly on props.

Imagine that we've composed the majority of a UI out of these simple presentational components -- all of them almost entirely stateless, all of them designed to do one thing and one thing well: they just receive props from their parent components and render! That's it. They are simple and beautiful and because they aren't doing much, because they are mostly stateless, they have a better chance of remaining blissfully bug free! This is the power and importance of presentational components. They are simple and they just work. So therefore we should strive to use them as much as possible.

Functional Component as a Stateless Function

A component that has no state means that our component doesn't even really need to be a JavaScript object of type Component at all. It can just be a simple function — one that takes an input and returns a (portion of) the UI.

const defaultLimit = 100

const TextField = (props) =>
  <input
    className="field field-light"
    onChange={props.onChange}
    limit={props.limit || defaultLimit}
  />;

With this our application is more predictable -- and here we can see the influence of the principles of functional programming on React -- that this function will always return the same UI output if given the same props. There are no state variables here that could be set to different values at different times that might lead the function to return something that we didn't predict. What we have here, then, is what in functional terms is called a "pure" or "referentially transparent" function. see what makes a function a true pure function

Separating Concerns Using a Container Component

class BookList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      books: []
    };
  }

  componentDidMount() {
    fetch('https://learn-co-curriculum.github.io/books-json-example-api/books.json')
      .then(response => response.json())
      .then(bookData => this.setState({ books: bookData.books }))
  }

  renderBooks = () => {
    return this.state.books.map(book => {
      return (
        <div className="book">
          <img src={ book.img_url } />
          <h3>{ book.title }</h3>
        </div>
      )
    })
  }

  render() {
    return (
      <div className="book-list">
        { this.renderBooks() }
      </div>
    )
  }
}

this component has tightly coupled together a whole set of assumptions about the data layer with another set of assumptions about the presentation layer. This is less than ideal.

breaking up the original component into a few pieces we would obtain the following: All the state is contained in our container component BookListContainer. The logic is the same, but it is has been uncoupled from the presentation layer, which is now contained in the BookList and Book components. Those components, which are stateless, are now incredibly stable as well as concise.

// src/Book.js
import React from 'react';

const Book = ({ title, img_url }) => (
  <div className="book">
    <img src={ img_url } alt={title}/>
    <h3>{ title }</h3>
  </div>
)

export default Book;


// src/BookList.js
import React from 'react';
import Book from './Book';

const BookList = ({ books }) => (
  <div className="book-list">
    { books.map(book => <Book title={book.title} img_url={book.img_url} />) }
  </div>
)

export default BookList;



// src/BookListContainer.js
import React from 'react';
import BookList from './BookList';

class BookListContainer extends React.Component {
  constructor() {
    super()

    this.state = {
      books: []
    };
  }

  componentDidMount() {
    fetch('https://learn-co-curriculum.github.io/books-json-example-api/books.json')
      .then(response => response.json())
      .then(bookData => this.setState({ books: bookData.books }))
  }

  render() {
    return <BookList books={this.state.books} />
  }
}

export default BookListContainer;

addendum on components

class ChildComponent extends Component {
  render() {
    return (
      <div>
        <p>Hey, I am a child</p>
        <p>My name is child component</p>
      </div>
    )
  }
}

class ParentComponent extends Component {
  render() {
    return (
      <div className="parent">
        <ChildComponent />
        <ChildComponent />
      </div>
    )
  }

results in:

<div class="parent">
  <div>
    <p>Hey, I am a child</p>
    <p>My name is child component</p>
  </div>
  <div>
    <p>Hey, I am a child</p>
    <p>My name is child component</p>
  </div>
</div>

but we can use React.Fragment

class ChildComponent extends Component {
  render() {
    //The wrapping 'div' here has been replaced with a React fragment
    return (
      <>
        <p>Hey, I am a child</p>
        <p>My name is child component</p>
      </>
    )
  }
}

class ParentComponent extends Component {
  render() {
    return (
      <div>
        <ChildComponent />
        <ChildComponent />
      </div>
    )
  }
}

results in:

<div>
    <p>Hey, I am a child</p>
    <p>My name is child component</p>
    <p>Hey, I am a child</p>
    <p>My name is child component</p>
</div>

Clone this wiki locally