React Hooks!

React Hooks!

React Salvador Meetup #11

React Salvador Meetup #11

Mobile Engineer @ Jusbrasil

lucianomlima

@lucianomlima

https://bit.ly/ReactSSA_invite

https://www.meetup.com/pt-BR/reactssa/

Software Engineer @ Agilize Contabilidade Online

RodolfoSilva

@Ro_DolfoSilva

React Salvador Meetup #11

Recapitulando

Biblioteca JavaScript para construção de Interfaces de Usuário (UI)

Baseada em composição de componentes

Usa JSX para extender o JavaScript

Um componente pode ter props e state

React Salvador Meetup #11

Stateless e Stateful Components

React Salvador Meetup #11

Stateful Components

ES6 classes

Métodos de ciclo de vida

Estado do componente

class Clock extends React.Component {
  state = { date: new Date() };

  componentDidMount() {
    this.timerID = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({ date: new Date() });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

React Salvador Meetup #11

Stateless Components

Componentes funcionais

Não possui métodos de ciclo de vida

Componentes não possuem estado

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

React Salvador Meetup #11

Stateless Components

Componentes funcionais

Não possui métodos de ciclo de vida

Componentes não possuem estado

Hooks

React Salvador Meetup #11

Hooks

O que é e

para que serve?

Facilitar o reuso de lógica entre componentes

Simplificar o código de componentes complexos

Diminuir o uso de classes

Permitir que componentes funcionais tenham estado e ciclo de vida

Não precisamos mais nos preocupar com escopo e uso de this

React Salvador Meetup #11

Hooks

import React, { useState } from 'react';

function Exemplo() {
  // Declara um novo estado chamado "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Você clicou {count} vezes</p>
      <button onClick={() => setCount(count + 1)}>
        Clique aqui
      </button>
    </div>
  );
}
import React, { useState } from 'react';

function Exemplo() {
  // Declara um novo estado chamado "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Você clicou {count} vezes</p>
      <button onClick={() => setCount(count + 1)}>
        Clique aqui
      </button>
    </div>
  );
}
import React from 'react';

class Exemplo extends React.Component {
  state = { count: 0 };

  incrementCount = () => {
    this.setState(state => ({
      count: state.count + 1
    }));
  };
  
  render() {
    const { count } = this.state;
    return (
      <div>
        <p>Você clicou {count} vezes</p>
        <button onClick={this.incrementCount}>
          Clique aqui
        </button>
      </div>
    );
  }
}
import { CurrentUserConsumer } from './current-user';
import ErrorHandlers from './error-handlers';

const CollectionEditorContainer = ({
  api, children, initialCollection
}) => (
  <ErrorHandlers>
    {errorFuncs => (
      <CurrentUserConsumer>
        {currentUser => (
          <CollectionEditor
            {...{ api, currentUser, initialCollection }}
            {...errorFuncs}
          >
            {children}
          </CollectionEditor>
        )}
      </CurrentUserConsumer>
    )}
  </ErrorHandlers>
);
import { useCurrentUser } from './current-user';
import useErrorHandlers from './error-handlers';

const CollectionEditorContainer = ({
  api, children, initialCollection
}) => {
  const { currentUser } = useCurrentUser();
  const errorFuncs = useErrorHandlers();
  return (
    <CollectionEditor
      {...{ api, currentUser, initialCollection }}
      {...errorFuncs}
    >
      {children}
    </CollectionEditor>
  );
};
import { CurrentUserConsumer } from './current-user';
import ErrorHandlers from './error-handlers';
import Uploader from './includes/uploader';
import { NotificationConsumer } from './notifications';

const TeamEditorContainer = ({
  api, children, initialTeam
}) => (
  <ErrorHandlers>
    {errorFuncs => (
      <Uploader>
        {uploadFuncs => (
          <NotificationConsumer>
            {notificationFuncs => (
              <CurrentUserConsumer>
                {(currentUser, fetched, { update }) => (
                  <TeamEditor
                    {...{ api, currentUser, initialTeam }}
                    updateCurrentUser={update}
                    {...uploadFuncs}
                    {...errorFuncs}
                    {...notificationFuncs}
                  >
                    {children}
                  </TeamEditor>
                )}
              </CurrentUserConsumer>
            )}
          </NotificationConsumer>
        )}
      </Uploader>
    )}
  </ErrorHandlers>
);
import { useCurrentUser } from './current-user';
import useErrorHandlers from './error-handlers';
import { useNotifications } from './notifications';
import useUploader from './includes/uploader';

const TeamEditorContainer = ({
  api, children, initialTeam
}) => {
  const { currentUser, update } = useCurrentUser();
  const uploadFuncs = useUploader();
  const notificationFuncs = useNotifications();
  const errorFuncs = useErrorHandlers();
  return (
    <TeamEditor
      {...{ api, currentUser, initialTeam }}
      updateCurrentUser={update}
      {...uploadFuncs}
      {...notificationFuncs}
      {...errorFuncs}
    >
      {children}
    </TeamEditor>
  );
};

Bundle menor

=

Maior performance

React Salvador Meetup #11

Principais Hooks

useState

useEffect

useContext

useReducer

Permite injetar estado no componente

Permite executar 'side effects' em um componente

Permite o componente se inscrever em um contexto React

Permite gerenciar estados complexos em um componente através de reducers

React Salvador Meetup #11

Dan's Quotes

Each Render Has Its Own Props and State

Each Render Has Its Own Event Handlers

Each Render Has Its Own Effects

Each Render Has Its Own... Everything

React Salvador Meetup #11

useState

Indicado para armazenar valores primitivos, nada muito complexo

Retorna sempre um valor e uma função para atualização do estado

Adiciona state a um Stateless Component

Componente Funcional

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count - 1)}>
        -
      </button>
      <button onClick={() => setCount(count + 1)}>
        +
      </button>
    </div>
  );
}
import React from 'react';

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

  decrement = () => {
    this.setState(state => ({ count: state.count - 1 }));
  };

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

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.decrement}>
          -
        </button>
        <button onClick={this.increment}>
          +
        </button>
      </div>
    );
  }
}
import React from 'react';

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

  decrement = () => {
    this.setState(state => ({ count: state.count - 1 }));
  };

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

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.decrement}>
          -
        </button>
        <button onClick={this.increment}>
          +
        </button>
      </div>
    );
  }
}
import React, { useState } from 'react';

function Counter() {
  // const [count, setCount] = useState(0);
  const state = useState(0);
  const count = state[0];
  const setCount = state[1];

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count - 1)}>
        -
      </button>
      <button onClick={() => setCount(count + 1)}>
        +
      </button>
    </div>
  );
}
import React, { useState } from 'react';

function MultipleStatesExample() {
  const [vehicle, setVehicle] = useState('car');
  const [foodList, setFoodList] = useState(['pizza', 'hotdog', 'kebab']);
  const [buttons, setButtons] = useState([
    { text: 'Change Vehicle', doAction: () => setVehicle('bike') },
    { text: 'Change FoodList', doAction: () => setFoodList(['hamburguer']) },
  ]);

  return (
    <div>
      <p>Your vehicle is a <strong>{vehicle.toUpperCase()}</strong></p>
      <p>Your favorite foods:</p>
      <ul>
        {foodList.map(item => <li>{item}</li>)}
      </ul>
      {buttons.map(bt => (
        <button onClick={bt.doAction}>{bt.text}</button>
      ))}
    </div>
  );
}

React Salvador Meetup #11

useEffect

É executado toda vez que o component é renderizado

O local ideal para controlar os 'sideEffects'

Serve ao mesmo propósito que os métodos:

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount
import React, { useEffect } from 'react';

function Lyrics() {
  // Similar ao componentDidMount e componentDidUpdate:
  useEffect(() => {
    document.title = 'São Salvador - Dorival Caymmi';
  });

  return (
    <p>
      São Salvador, Bahia de São Salvador<br />
      A terra de Nosso Senhor<br />
      Pedaço de terra que é meu<br />
      ...
    </p>
  );
}

Em todo render o useEffect será executado (inclusive no primeiro)

Por ser declarado dentro do componente, useEffect tem acesso a todos os states e props

import React from 'react';

class Lyrics extends React.Component {
  componentDidMount() {
    document.title = 'São Salvador - Dorival Caymmi';
  }

  render() {
    return (
      <p>
        São Salvador, Bahia de São Salvador<br />
        A terra de Nosso Senhor<br />
        Pedaço de terra que é meu<br />
        ...
      </p>
    );
  }
}
import React, { useEffect } from 'react';

export function Letra() {
  useEffect(() => {
    document.title = 'São Salvador - Dorival Caymmi';
  });

  return (
    <p>
      São Salvador, Bahia de São Salvador<br />
      A terra de Nosso Senhor<br />
      Pedaço de terra que é meu<br />
      ...
    </p>
  );
}
import React, { useEffect, useState } from 'react';

const originalPageTitle = document.title;

function Lyrics() {
  const [title] = useState('São Salvador - Dorival Caymmi');

  useEffect(() => {
    document.title = `${title} | ${originalPageTitle}`;

    // Similar ao componentWillUnmount:
    return () => {
      document.title = originalPageTitle;
    };
  });

  return (
    <div>
      <h1>{title}</h1>
      <p>
        São Salvador, Bahia de São Salvador<br />
        A terra de Nosso Senhor<br />
        Pedaço de terra que é meu<br />
        ...
      </p>
    </div>
  );
}

Retorna o título original quando o componente é desmontado

import React from 'react';

const originalPageTitle = document.title;

class Lyrics extends React.Component {
  state = {
    title: 'São Salvador - Dorival Caymmi',
  };

  componentDidMount() {
    document.title = `${this.state.title} | ${originalPageTitle}`;
  }

  componentWillUnmount() {
    document.title = originalPageTitle;
  }

  render() {
    return (
      <p>
        São Salvador, Bahia de São Salvador<br />
        A terra de Nosso Senhor<br />
        Pedaço de terra que é meu<br />
        ...
      </p>
    );
  }
}
import React, { useEffect, useState } from 'react';

const originalPageTitle = document.title;

function Lyrics() {
  const [title] = useState('São Salvador - Dorival Caymmi');

  useEffect(() => {
    document.title = `${title} | ${originalPageTitle}`;

    return () => {
      document.title = originalPageTitle;
    };
  });

  return (
    <p>
      São Salvador, Bahia de São Salvador<br />
      A terra de Nosso Senhor<br />
      Pedaço de terra que é meu<br />
      ...
    </p>
  );
}
import React, { useEffect, useState } from 'react';

const originalPageTitle = document.title;

function Lyrics() {
  const [title] = useState('São Salvador - Dorival Caymmi');

  useEffect(() => {
    document.title = `${title} | ${originalPageTitle}`;

    return () => {
      document.title = originalPageTitle;
    };

  // Evita que o side effect seja executado em todo render
  }, []);

  return (
    <p>
      São Salvador, Bahia de São Salvador<br />
      A terra de Nosso Senhor<br />
      Pedaço de terra que é meu<br />
      ...
    </p>
  );
}
import React from 'react';

const originalPageTitle = document.title;

class Lyrics extends React.Component {
  state = {
    title: 'São Salvador - Dorival Caymmi',
  };

  componentDidMount() {
    document.title = `${this.state.title} | ${originalPageTitle}`;
  }

  componentWillUnmount() {
    document.title = originalPageTitle;
  }

  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <p>
        São Salvador, Bahia de São Salvador<br />
        A terra de Nosso Senhor<br />
        Pedaço de terra que é meu<br />
        ...
      </p>
    );
  }
}
import React, { useEffect, useState } from 'react';

const originalPageTitle = document.title;

function Lyrics() {
  const [title] = useState('São Salvador - Dorival Caymmi');

  useEffect(() => {
    document.title = `${title} | ${originalPageTitle}`;

    return () => {
      document.title = originalPageTitle;
    };

  // Evita que o side effect seja executado em todo render
  }, []);

  return (
    <p>
      São Salvador, Bahia de São Salvador<br />
      A terra de Nosso Senhor<br />
      Pedaço de terra que é meu<br />
      ...
    </p>
  );
}
import React, { useEffect, useState } from 'react';

const originalPageTitle = document.title;

function Lyrics() {
  const [title] = useState('São Salvador - Dorival Caymmi');

  useEffect(() => {
    document.title = `${title} | ${originalPageTitle}`;

    return () => {
      document.title = originalPageTitle;
    };

  // O side effect só será executado quando title mudar
  }, [title]);

  return (
    <p>
      São Salvador, Bahia de São Salvador<br />
      A terra de Nosso Senhor<br />
      Pedaço de terra que é meu<br />
      ...
    </p>
  );
}
import React from 'react';

const originalPageTitle = document.title;

class Lyrics extends React.Component {
  state = {
    title: 'São Salvador - Dorival Caymmi',
  };

  componentDidMount() {
    document.title = `${this.state.title} | ${originalPageTitle}`;
  }

  componentWillUnmount() {
    document.title = originalPageTitle;
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.title !== this.state.title) {
      document.title = `${this.state.title} | ${originalPageTitle}`;
    }
  }

  render() {
    return (
      <p>
        São Salvador, Bahia de São Salvador<br />
        A terra de Nosso Senhor<br />
        Pedaço de terra que é meu<br />
        ...
      </p>
    );
  }
}
import React, { useEffect, useState } from 'react';

const originalPageTitle = document.title;

function Lyrics() {
  const [title] = useState('São Salvador - Dorival Caymmi');

  useEffect(() => {
    document.title = `${title} | ${originalPageTitle}`;

    return () => {
      document.title = originalPageTitle;
    };
  }, [title]);

  return (
    <p>
      São Salvador, Bahia de São Salvador<br />
      A terra de Nosso Senhor<br />
      Pedaço de terra que é meu<br />
      ...
    </p>
  );
}

React Salvador Meetup #11

useContext

Espera que seja passado como parâmetro o object criado pelo React.createContext();

Retorna o valor atual do contexto passado

O mesmo comportamento do Context.Consume porém sem o Hadouken do Ryu

import { CurrentUserConsumer } from './current-user';
import ErrorHandlers from './error-handlers';

const CollectionEditorContainer = ({
  api, children, initialCollection
}) => (
  <ErrorHandlers>
    {errorFuncs => (
      <CurrentUserConsumer>
        {currentUser => (
          <CollectionEditor
            {...{ api, currentUser, initialCollection }}
            {...errorFuncs}
          >
            {children}
          </CollectionEditor>
        )}
      </CurrentUserConsumer>
    )}
  </ErrorHandlers>
);
import React, { useContext } from 'react';
import { CurrentUserContext } from './current-user';
import { ErrorHandlersContext } from './error-handlers';

const CollectionEditorContainer = ({
  api, children, initialCollection
}) => {
  const currentUser = useContext(CurrentUserContext);
  const errorFuncs = useContext(ErrorHandlersContext);
  return (
    <CollectionEditor
      {...{ api, currentUser, initialCollection }}
      {...errorFuncs}
    >
      {children}
    </CollectionEditor>
  );
};

React Salvador Meetup #11

useReducer

Eu ouvi redux? 🤐

Alternativa ao useState quando se tem uma lógica complexa de estado do componente.

API semelhante ao Redux (reducer, initialState, dispatch)

Diferentemente do Redux, não permite connect (o state é local)

Pode ser combinado com a Context API para compartilhamento de estado entre componentes

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter({ initialState }) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: { state.count }
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

React Salvador Meetup #11

useReducer

Você pode criar o initialState de forma dinâmica (Lazy initialization)

function init(initialCount) {
  return { count: initialCount };
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({ initialCount }) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: { state.count }
      <button
        onClick={() => dispatch({ type: 'reset', payload: initialCount })}>
        Reset
      </button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

Permite extrair a lógica de criação do initialState para fora do reducer, diminuindo o acoplamento.

React Salvador Meetup #11

useReducer

Ao retornar o mesmo valor como estado atual para um hook useReducer, o React ignorará e não será feito um novo render e nem disparando os side effects

// TodosApp.js
export const TodosDispatch = React.createContext(null);

function TodosApp() {
  // `dispatch` não muda entre re-renders
  const [todos, dispatch] = useReducer(todosReducer);

  return (
    <TodosDispatch.Provider value={dispatch}>
      <DeepTree todos={todos} />
    </TodosDispatch.Provider>
  );
}

// ------------------------------------------------------

// DeepChild.js
function DeepChild(props) {
  // Para dispararmos uma action, obtemos o dispatch do contexto.
  const dispatch = useContext(TodosDispatch);

  function handleClick() {
    dispatch({ type: 'add', text: 'hello' });
  }

  return (
    <button onClick={handleClick}>Add todo</button>
  );
}

React Salvador Meetup #11

Custom Hooks

Permite o compartilhamento de recursos

Pode ser utilizado dentro dele todos os outros hooks

É apenas uma função que encapsula uma funcionalidade podendo ou não ter 'sideEffects'

Assim como os outros hooks, só pode ser utilizado dentro de componentes funcionais

import { useState, useEffect } from 'react';

export function useCounter(initialCount) {
  const [count, setCount] = useState(initialCount);

  const handleIncrement = () => setCount(prevCount => prevCount + 1);
  const handleDecrement = () => setCount(prevCount => prevCount - 1);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return {
    count,
    setCount,
    increment: handleIncrement,
    decrement: handleDecrement
  };
};

React Salvador Meetup #11

Fluxo de execução  dos Hooks 

Inicialização

Primeira renderização

Atualização

Destruição

let firstRender = true;

function RenderFunctionComponent() {
  let initName;
  
  if(firstRender){
    [initName] = useState("Rudi");
    firstRender = false;
  }
  const [firstName, setFirstName] = useState(initName);
  const [lastName, setLastName] = useState("Yardley");

  return (
    <Button onClick={() => setFirstName("Fred")}>Fred</Button>
  );
}

React Salvador Meetup #11

Testing Hooks

import { useState, useEffect } from 'react';

export function useCounter(initialCount) {
  const [count, setCount] = useState(initialCount);

  const handleIncrement = () => setCount(count + 1);
  const handleDecrement = () => setCount(count - 1);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return {
    count,
    setCount,
    increment: handleIncrement,
    decrement: handleDecrement
  };
};
import React from 'react';
import { useCounter } from './useCounter';

export function Counter() {
  const counter = useCounter(0);

  return (
    <div>
      <p>You clicked {counter.count} times</p>
      <button id="decrement" onClick={counter.decrement}>
        -
      </button>
      <button id="increment" onClick={counter.increment}>
        +
      </button>
    </div>
  );
}
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import { Counter } from './Counter';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
});
it('can render and update a counter', () => {
  act(() => {
    ReactDOM.render(<Counter />, container);
  });

  const incrementButton = container.querySelector('button#increment');
  const decrementButton = container.querySelector('button#decrement');
  const label = container.querySelector('p');

  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');

  act(() => {
    incrementButton.dispatchEvent(new MouseEvent('click', { bubbles: true }));
  });

  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');

  act(() => {
    decrementButton.dispatchEvent(new MouseEvent('click', { bubbles: true }));
  });

  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');
});
it('can render and update a counter', () => {
  act(() => {
    ReactDOM.render(<Counter />, container);
  });

  const incrementButton = container.querySelector('button#increment');
  const decrementButton = container.querySelector('button#decrement');
  const label = container.querySelector('p');

  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');

  act(() => {
    incrementButton.dispatchEvent(new MouseEvent('click', { bubbles: true }));
  });

  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');

  act(() => {
    decrementButton.dispatchEvent(new MouseEvent('click', { bubbles: true }));
  });

  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');
});

React Salvador Meetup #11

Quando usar classes

Quando sua classe depender dos seguintes métodos do ciclo de vida do componente:

  • getSnapshotBeforeUpdate
  • componentDidCatch

React Salvador Meetup #11

E os HOC's e Render Props? 🤔

E os HOC's e Render Props? 🤔

Para a grande maioria dos casos, Hooks devem substituir esses padrões.

Não há real necessidade de reescrever seus hocs e render props components.

Mas sua aplicação se beneficiará de um código mais simples, legível e com bundle menor.

React Salvador Meetup #11

E static typing? (TS / Flow)

Hooks são apenas funções.

Isto permite adicionar tipos muito mais facilmente que HOC's.

As versões mais recentes de Flow e TypeScritpt já suportam hooks.

React Salvador Meetup #11

Resumo

  • Completamente opt-in. Você pode testar sem precisar reescrever seu código atual.
  • Você sequer precisa aprender hooks caso não queira.
  • 100% backwards-compatible. Sem breaking changes.
  • Hooks está disponível a partir do React 16.8.0.
  • Não há planos para remover classes do React.
  • Hooks não vai substituir o que você já sabe sobre React.
  • Hooks provê uma API mais direta aos conceitos que você já conhece do React: props, state, context, refs, e lifecycle.
  • Completamente opt-in. Você pode testar sem precisar reescrever seu código atual.
  • Você sequer precisa aprender hooks caso não queira.
  • 100% backwards-compatible. Sem breaking changes.
  • Hooks está disponível a partir do React 16.8.0.
  • Não há planos para remover classes do React.
  • Hooks não vai substituir o que você já sabe sobre React.
  • Hooks provê uma API mais direta aos conceitos que você já conhece do React: props, state, context, refs, e lifecycle.

React Salvador Meetup #11

Regras Básicas

Hooks devem estar sempre no level mais alto

Não chame um hook dentro de lops, condições ou funções aninhadas.

Hooks precisam ser chamados sempre na mesma ordem a cada nova renderização.

Isso permitirá que o state seja preservado quando tiver múltiplos states e effects.

React Salvador Meetup #11

Regras Básicas

Hooks devem ser chamados somente dentro de componentes funcionais

Não chame hooks dentro de funções JavaScript comuns.

Hooks podem ser chamados dentro de outros hooks.

React Salvador Meetup #11

Outros Hooks

useCallback

useMemo

useRef

useImperativeHandle

useLayoutEffect

useDebugValue

React Salvador Meetup #11

Outros Hooks

useCallback

// returns a memoized callback
// useCallback(fn: () => void, inputs: [])
// equivalent to useMemo(() => fn, inputs);

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

React Salvador Meetup #11

Outros Hooks

useMemo

// returns a memoized value
// useMemo(() => fn: () => any, inputs: []);

function computeExpensiveValue(a: number, b: number): number {
  // very expensive
  return a + b;
}

const memoizedValue = useMemo(
  () => computeExpensiveValue(a, b),
  [a, b]
);

React Salvador Meetup #11

Outros Hooks

useRef

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={() => { inputEl.current.focus(); }}>
        Focus the input
       </button>
    </div>
  );
}

React Salvador Meetup #11

Outros Hooks

useImperativeHandle

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}

// Should be used with forwardRef
FancyInput = forwardRef(FancyInput);

React Salvador Meetup #11

Outros Hooks

useLayoutEffect

React Salvador Meetup #11

Outros Hooks

useDebugValue

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  // ...

  // Show a label in DevTools next to this Hook
  // e.g. "FriendStatus: Online"
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

React Salvador Meetup #11

Referências e Docs

https://reactjs.org/docs/hooks-intro.html – Documentação Oficial

https://overreacted.io/ – Dan Abramov blog

https://kentcdodds.com/blog – Kent C. Dodds blog

React Salvador Meetup #11