React: Não use o useEffect?
Esse post não busca te convencer a parar de usar o useEffect, até porque não tem como não usar, mas nem sempre é preciso dele e eu quero te mostrar como e porque. E isso ainda vai te ajudar a deixar seu código mais limpo 😉, o famoso clean code.
O que vamos usar?
Suspense (React) + useSWR (Vercel ❤️).
Quando removemos o useEffect?
A primeira requisição que a página ou o componente faz.
O fluxo que aprendemos normalmente quando queremos fazer uma requisição assim que a página carrega é da seguinte forma:
...
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get("/url-da-api")
.then(({ data }) => data)
.then((resUsers) => setUsers(resUsers));
}, []);
...
Certo, e se você quiser um loading? Aí precisa adicionar um loading state e setar como false ao fim da requisição:
...
const [users, setUsers] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
axios.get("/url-da-api")
.then(({ data }) => data)
.then((resUsers) => setUsers(resUsers))
.finally(() => setIsLoading(false));
}, []);
...
Também não pode deixar de tratar o catch até porque não vivemos em mundo perfeito.
...
const [users, setUsers] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
useEffect(() => {
axios.get("/url-da-api")
.then(({ data }) => data)
.then((resUsers) => setUsers(resUsers))
.catch(() => setHasError(true))
.finally(() => setIsLoading(false));
}, []);
...
E isso tudo porque nem falamos de UI ainda, e todo o código para fazer isso ficar agradável...
Agora sim! Como fariamos sem o useEffect?
Primeiro você precisa instalar o useSWR, também é muito importante olhar a documentação (depois de ler e aplicar esse conteúdo).
npm install swr
ou yarn add swr
E depois você pode importar
import useSWR from swr;
Eu geralmente uso da seguinte forma para evitar que ele fique revalidando (refazendo a requisição para ver se tem algum conteúdo novo)
import useSWR from "swr/immutable";
Depois disso você consegue substituir nosso useEffect (e de bônus o useState também) por:
...
const fetcher = (url) => axios.get(url)
.then(({ data }) => data);
const { data:users } = useSWR("/url-da-api", fetcher, { suspense: true });
...
É isso... Simples assim, mas nesse ponto seu código NÃO VAI ESTAR FUNCIONARNDO, pois agora que vem o React.Suspense. Percebe o ...{ suspense: true });
ao final do nosso código? Ele serve para deixar o nosso componente em estado de loading. Mas como vamos mostrar um "Carregando..." ou um Skeleton?
Aplicando o React.Suspense
Vamos dizer que o nosso código faz parte de um componente UsersTable
, e nesse momento estamos carregando ele dentro do UsersPage
.
...
function UsersPage() {
return (
<div>
<h1>Users</h1>
<UsersTable />
</div>
);
}
...
Ao carregar o <UsersTable />
, nesse momento ele vai quebrar a página pois ele está fazendo a requisição na api e em momento algum foi tratado isso no código.
Primeiro vamos importar o Suspense diretamente do React:
import { Suspense } from "react";
...
E envolver nosso componente <UsersTable />
em um Suspense
:
...
<Suspense>
<UsersTable />
<Suspense>
...
E por fim adicionar um atributo fallback
, com componente ou tag, ao suspense para mostrar o "Carregando...".
...
<Suspense fallback={<span>Carregando...</span>}>
<UsersTable />
<Suspense>
...
E está pronto, agora é só dá uma conferida na documentação do useSWR para ver mais forma de usar, e você pode até mesmo usar sem o Suspense.
Dica extra: E se a requisição falhar? 🤔
Boa, então você se preocupa com diferentes cenários né?! Se a nossa "inquebrável" api quebrar o que acontece? Sua página inteira quebra!
Mas não precisa ser assim, e em conjunto com tudo que aprendemos também vamos ver como resolver essa situação, e tornar seu front inquebrável.
Vamos começar instalando o ErrorBoundary:
npm install react-error-boundary
ou yarn add react-error-boundary
E depos você pode importar:
import { ErrorBoundary } from "react-error-boundary";
...
Da mesma forma como você usou o Suspense você vai usar o ErrorBoundary. Ou seja, envolve o componente nele e passa um fallback, e vale a pena saber que ele vai pegar todos os erros do código, não só o dá requisição.
...
<ErrorBoundary fallback={<span>Ops! Algo deu errado!</span>}>
<Suspense fallback={<span>Carregando</span>}>
<UsersTable />
<Suspense>
<ErrorBoundary fallback={<span>Carregando</span>}>
...
O mais legal disso tudo é que o UsersPage
não vai quebrar como normalmente ocorre quando um componente interno quebra, apenas o seu UsersTable
vai quebrar de forma isolada, mostrando o conteúdo do fallback: <span>Ops! Algo deu errado!</span>
Dica: Para debuggar componentes que estão envolvidos em ErrorBoundary, use o console do seu browser.