React Hooks - Part 1

Content

  • Case of study
  • Why
  • What

> Init

> Case of study

+ Step = branch

Class Components

  • ES5
  • Adopted by convention (idiomatic way of writing js).

> Why ? > Before

import React, { Component } from "react";

export default (CatParts) =>
  class CatBoddy extends Component {

    constructor(props) {
      super(props);
      this.state = { requestId: 0}

      this.onClick = this.onClick.bind(this);
    }

    onClick(ev) {
      const requestId = Math.random().toString(25).substring(10)
      this.setState({requestId});
    }

    render() {
      return (
        <div className="list">
          <button className="btn" onClick={this.onClick}>
            Paw here to get random cats
          </button>
          {<CatParts requestId={this.state.requestId} />}
        </div>
      );
    }
  };

"Trade-offs":  

- Binding

- Super

- Duplicate code

> Why ? > Before > Duplicated code

import React, { Component } from 'react';
import Cat from './Cat';
import catRequest from '../network/catRequest';

class CatList extends Component {
  // TODO: use proptypes checker.

  constructor(props) {
    super(props);
    this.state = {
      catList: [],
    } 
  }

  async componentDidMount () {
    const data = await catRequest(9);

    this.setState({catList: data});
  }

  async componentDidUpdate (prevProps, prevState) {
    if (prevProps.requestId !== this.props.requestId) {
      const data = await catRequest(9);
      this.setState({catList: data});
    }
  }

  render() {
    return (
      <section className='wrapper'>
        {this.state.catList.map(cat => <Cat key={cat.id} url={cat.url} />)}
      </section>
    )
  }
}

export default CatList;

> Why ? > Before > Duplicated code solution (HOC) + autobinding + super (Class Fields)

export default (CatParts) =>
  class CatBoddy extends Component {
    state = {
      catList: [],
      requestId: 0,
    };

    async componentDidMount() {
      const data = await catRequest(9);

      this.setState({ catList: data });
    }

    // This is done for educational purposes, yes, I know it is not the best use case
    async componentDidUpdate(prevProps, prevState) {
      if (prevState.requestId !== this.state.requestId) {
        const data = await catRequest(9);
        this.setState({ catList: data });
      }
    }

    // Class Fields
    onClick = (ev) => {
      const requestId = Math.random().toString(25).substring(10);
      this.setState({ requestId });
    }

    render() {
      return (
        <div className="list">
          <button className="btn" onClick={this.onClick}>
            Paw here to get random cats
          </button>
          {
            <CatParts
              requestId={this.state.requestId}
              catList={this.state.catList}
            />
          }
        </div>
      );
    }
  };
import React, { Component } from 'react';
import Cat from './Cat';
import CatBoddy from './CatBoddy';

function CatList({catList}) {
  return (
    <section className='wrapper'>
      {catList.map(cat => <Cat key={cat.id} url={cat.url} />)}
    </section>
  );
}

export default CatBoddy(CatList);

/*
	How about if we have more components who receive the same properties? 
*/

function CatList({catList}) {
	...
}

export default CatBoddy(CatList1);
....
export default CatBoddy(CatListFooter);

export default CatHOC1(CatHOC2(CatHOC3.....))

> Why ? > Before > But ... wrapper hell

Wrapper hell

> Why ? > Before > Reder props ?

Render Props

Class Components

> Why ? > overview

import React, { Component } from "react";

export default (CatParts) =>
  class CatBoddy extends Component {

    constructor(props) {
      super(props);
      this.state = { requestId: 0}

      this.onClick = this.onClick.bind(this);
    }

    onClick(ev) {
      const requestId = Math.random().toString(25).substring(10)
      this.setState({requestId});
    }

    render() {
      return (
        <div className="list">
          <button className="btn" onClick={this.onClick}>
            Paw here to get random cats
          </button>
          {<CatParts requestId={this.state.requestId} />}
        </div>
      );
    }
  };

- Binding

- Super

- Duplicate code

- Wrapper hell

- Multiple life-cycles

Classic Approach

> Why ? > Before vs Hooks

Need state ? = Class

Not State ? = Functional Stateless Components

Hooks Approach

Not State / State = Function components! *

Functions are simple!

Hooks

 

Hooks are functions that let you “hook into” React state and lifecycle features from function components.

 

 

 

> What ? > Hooks

Basic Hooks

  • Use State
  • Use Efect
  • Use Context
import React, { useState, useEffect } from 'react';
import Cat from './Cat';
import catRequest from "../network/catRequest";

function CatList() {
  const [catData, setCatData] = useState([]);
  const [requestId, setRequestId] = useState(0);

  useEffect(() => {
    fetchCats();
  }, [requestId]);

  async function fetchCats () {
    const data = await catRequest(10);
    setCatData(data);
  }

  // fetch more cats
  const onClick = (ev) => {
    const requestId = Math.random().toString(25).substring(10);
    setRequestId(requestId);
  }

  return (
    <div className="list">      
      <button className="btn" onClick={onClick}>
        Paw here to get random cats
      </button> 
      <section className='wrapper'>
        {catData.map(cat => <Cat key={cat.id} url={cat.url} />)}
      </section>
    </div>
  );
}

export default CatList;

> What ? > Hooks > Basics

Use state

  • As this. setState but does not merge the old vs new state.
  • Returns current state and function.
  • Call useState how many times do you need it.
  const [catData, setCatData] = useState([]);
  const [requestId, setRequestId] = useState(0);

...

  async function fetchCats () {
    const data = await catRequest(10);
    setCatData(data);
  }

  // fetch more cats
  const onClick = (ev) => {
    const requestId = Math.random().toString(25).substring(10);
    setRequestId(requestId);
  }

> What ? > Hooks > Basics

Use efect

  • Adds the ability to perform side effects from a function component.
  • Same propose to: componentDidMount, componentDidUpdate, and componentWillUnmount
  const [catData, setCatData] = useState([]);
  const [requestId, setRequestId] = useState(0);

  useEffect(() => {
    fetchCats();
  }, [requestId]);

  async function fetchCats () {
    const data = await catRequest(10);
    setCatData(data);
  }

> What ? > Hooks > Basics

Use Context

subscribe to React context without introducing nesting

import React, { useContext } from "react";
import { GlobalContext } from "../context/GlobalState";

export const Balance = () => {
  const { transactions } = useContext(GlobalContext);

  const amounts = transactions.map(transaction => transaction.amount);
  const total = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2);

  return (
    <>
      <h4>Your Balance</h4>
      <h1>${total}</h1>
    </>
  );
};

> What ? > Hooks > Basics

Custom Hooks

> Reuse everywhere!

 Extract component logic into reusable functions.

export default function useGetCatsData (requestId, catNumber) {
  const [catData, setCatData] = useState([]);

  useEffect(() => {
    fetchCats();
  }, [requestId]);

  async function fetchCats () {
    const data = await catRequest(catNumber);
    setCatData(data);
  }

  return catData;
}
function CatList() {
  const [requestId, setRequestId] = useState(0);
  const catData = useGetCatsData(requestId, 10);

  // fetch more cats
  const onClick = (ev) => {
    const requestId = Math.random().toString(25).substring(10);
    setRequestId(requestId);
  }

  return (
    <div className="list">      
      <button className="btn" onClick={onClick}>
        Paw here to get random cats
      </button> 
      <section className='wrapper'>
        {catData.map(cat => <Cat key={cat.id} url={cat.url} />)}
      </section>
    </div>
  );
}

Rules of Hooks

  • Call Hooks from React function components.
  • Don’t call Hooks inside loops, conditions, or nested functions.
import React, { useState, useEffect } from 'react';
import Cat from './Cat';
import catRequest from "../network/catRequest";

function CatList() {
  const [catData, setCatData] = useState([]);
  const [requestId, setRequestId] = useState(0);

  useEffect(() => {
    fetchCats();
  }, [requestId]);

  async function fetchCats () {
    const data = await catRequest(10);
    setCatData(data);
  }

  // fetch more cats
  const onClick = (ev) => {
    const requestId = Math.random().toString(25).substring(10);
    setRequestId(requestId);
  }

  return (
    <div className="list">      
      <button className="btn" onClick={onClick}>
        Paw here to get random cats
      </button> 
      <section className='wrapper'>
        {catData.map(cat => <Cat key={cat.id} url={cat.url} />)}
      </section>
    </div>
  );
}

export default CatList;

> Hooks Rules!

Conclusions

> Reuse everywhere!

  • Reuse logic: mitigate life-cycle bugs.
  • Avoid use of bind and super (class component).
  • No more thinking about state vs props.
  • Mitigate wrapper hell.
  • Easy to use and understand.
  • Functions, functions everywhere (few of lightweight life-cycles).

Gracias!

Reack hooks part 1

By Daniel Diaz Giraldo

Reack hooks part 1

  • 148