
Você já teve dificuldades em compartilhar estados entre seus components do React? Ou já teve dificuldades na hora de usar o Redux? O Recoil chegou para te ajudar
O Recoil é uma biblioteca criada dentro do Facebook (assim como o próprio React) que tem como objetivo te ajudar a gerenciar estados de uma forma fácil e intuitiva (usar o recoil é como usar um hook qualquer no React).
Neste artigo eu vou te apresentar os principais pontos do Recoil e te mostrar através de um exemplo prático como utiliza-lo na suas aplicações, então pega seu café, respira fundo e vem comigo.
Entendendo o Recoil
Na versão atual o Recoil é baseado principalmente em dois pilares, os Atoms e os Selectors, vamos falar um pouco deles para você compreender a mágia por trás desta lib de gerenciamento de estados do React.
Atoms
Atoms são unidades de estado, é possível atualizar e ler estes estados de uma forma fácil.
Também é possível conectar seus components a estes Atoms para que quando eles sejam atualizados os components sejam renderizados novamente.
Você também pode usar os Atoms ao invés dos estados locais dos components e utiliza-los para compartilhar os estados entre muitos components.
Exemplo: Declarando um Atom (estado) para armazenar o tamanho de uma fonte de texto:
1 2 3 4 |
const fontSizeState = atom({ key: 'fontSizeState', default: 14, }); |
Obs: Use chaves únicas para definir seus Atoms (isso ajuda no debug e evita problema no compartilhamento de estados)
Usando um Atom:
1 2 3 4 |
function Text() { const [fontSize, setFontSize] = useRecoilState(fontSizeState); return <p style={{fontSize}}>This text will increase in size too.</p>; } |
Ps: Note como a experiencia é a mesma se usar o hook useState.
Atualizando um Atom (usando o hook useRecoilState):
1 2 3 4 5 6 7 8 |
function FontButton() { const [fontSize, setFontSize] = useRecoilState(fontSizeState); return ( <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}> Click to Enlarge </button> ); } |
Ps: Esses exemplos vem da 🙂
Selectors
Os Selectors sâo funções puras (que tem como objetivo devolver valores derivados dos Atoms) que recebem um Atom como argumento, quando o Atom que veio como argumento é atualizado o selector também atualiza o valor de retorno.
Assim como no caso dos Atoms, os Components também podem se ‘inscrever’ para serem avisados quando os selectors forem atualizados, quando isso acontece eles são renderizados novamente.
Declarando um Selector:
1 2 3 4 5 6 7 8 9 |
const fontSizeLabelState = selector({ key: 'fontSizeLabelState', get: ({get}) => { const fontSize = get(fontSizeState); const unit = 'px'; return `${fontSize}${unit}`; }, }); |
Obs: O get é usado para chamar o Atom associado, quando um get é declarado dentro do Selector ele gera uma ligação entre eles, quando o Atom for atualizado o Selector também será.
Usando um Selector:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function FontButton() { const [fontSize, setFontSize] = useRecoilState(fontSizeState); const fontSizeLabel = useRecoilValue(fontSizeLabelState); return ( <> <div>Current font size: ${fontSizeLabel}</div> <button onClick={() => setFontSize(fontSize + 1)} style={{fontSize}}> Click to Enlarge </button> </> ); } |
Ps: Esses exemplos vem da 🙂
Recoil na Prática
Neste artigo nós vamos criar uma sessão de comentários (lista com comentários anteriores e campo para escrever um novo) que terá seus estados gerenciados através do Recoil, neste exemplo vamos demonstrar como organizar seus atoms e selectors e como utiliza-los nos components de forma fácil.
Dependências
-
NodeJs
-
Npm ou Yarn
Passo a Passo
Gerando o Projeto e instalando o Recoil
1 – Para gerar o projeto rode:
1 |
npx create-react-app comments |
2 – Entre no projeto:
1 |
cd comments |
3 – Instale o Recoil rodando:
Npm
1 |
npm install recoil |
Yarn
1 |
yarn add recoil |
4 – Crie a pasta de components, o component Comments, o component Comment e o Component Form rodando:
1 2 3 4 5 6 7 8 9 10 |
mkdir src/components mkdir src/components/Comments touch src/components/Comments/index.js mkdir src/components/Comments/Comment touch src/components/Comments/Comment/index.js mkdir src/components/Comments/Form touch src/components/Comments/Form/index.js |
Detalhes:
- – O component Comments vai exibir a lista de comentários e um formulário para a criação de um novo comentário.
- – O component Comment vai exibir um único comentário (ele será chamado por comments)
- – O component Form vai permitir a criação de um novo comentário (ele será chamado por comments)
5 – No index.js do comments coloque a base do component:
1 2 3 4 5 6 7 8 9 10 11 12 |
import React, {Fragment} from 'react'; function Comments() { return ( <Fragment> Comments list </Fragment> ); } export default Comments; |
6 – No index.js do comment coloque a base do component:
1 2 3 4 5 6 7 8 9 10 11 12 |
import React from 'react'; function Comment() { return ( <div> Comment details </div> ); } export default Comment; |
7 – No index.js do form coloque a base do component:
1 2 3 4 5 6 7 8 9 10 11 12 |
import React from 'react'; function Form() { return ( <div> New Comment </div> ); } export default Form; |
8 – Em app.js coloque:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import React from 'react'; import Comments from './components/Comments'; import { RecoilRoot } from 'recoil'; function App() { return ( <RecoilRoot> <Comments /> </RecoilRoot> ); } export default App; |
Detalhes:
- – Importamos e incluimos o component Comments que vai exibir a lista de comentários e o formulário para criar um novo comentário.
- – Todos os components que vão usar o Recoil precisam ter como pai na arvore de components o RecoilRoot, por isso importamos ele e incluimos em volta do component Comments (e de todos os possíveis components que usariamos).
Preparando os components
1 – No components Comments coloque:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import React, {Fragment} from 'react'; import Comment from './Comment'; import Form from './Form'; function Comments() { return ( <Fragment> <div> <h2>3 Comentários</h2> <hr /> <Comment username='Leonardo' text='Este artigo é realmente muito bom!'/> <Comment username='Rachel' text='Me tirou muitas dúvidas, obrigada!'/> <Comment username='Jule' text='No próximo comente mais sobre side effects no Recoil'/> </div> <div> <Form /> </div> </Fragment> ); } export default Comments; |
Detalhes:
- – Importamos o component Comment (para exibir a lista de comentários)
- – Chamos o component Comment três vezes passando o username (de forma fixa inicialmente, depois vamos mostrar os comentários usando os estados)
2 – No component Comment coloque:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import React from 'react'; function Comment(props) { return ( <div> <p> <strong>{props.username}:</strong> {props.text} </p> <hr /> </div> ); } export default Comment; |
Detalhes:
- – Incluímos o recebimento de props (onde receberemos o username e o texto do comentário)
- – Exibimos o username e o texto recebido de uma forma simples
3 – No component Form coloque:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React from 'react'; function Form() { return ( <div> <form> <div> <strong>Deixe seu comentário:</strong> </div> <div> <input type="text" name="username" placeholder='Nome de usuário' /> </div> <div> <textarea type="text" name="text" placeholder='Seu comentário...' /> </div> <input type="submit" value="Enviar" /> </form> </div> ); } export default Form; |
Detalhes:
- – Nós criamos uma estrutura básica de um formulário para o envio de comentários
4 – Suba o servidor rodando:
Npm
1 |
npm start |
Yarn
1 |
yarn start |
Criando e consumindo o Atom
1 – Crie a estrutura de pastas onde ficaram os atoms e selectors rodando:
1 2 3 |
mkdir src/stores mkdir src/stores/atoms mkdir src/stores/selectors |
Detalhes:
-
Eu prefiro organizar elementos diferentes em pastas diferentes. Por exemplo, eu separo ou components em uma pasta, os atoms e selectors em outras e etc, mas é comum ver no mundo Js a separação por feature, ou seja, colocar tudo que tem a ver com comentários em uma única pasta.
-
Não existe uma maneira necessariamente mais correta, mas a minha experiencia pessoal aponta que é mais fácil entender toda a sua estrutura quando ela está separada pela natureza dos elementos 🙂
2 – Crie agora os arquivos onde vão ficar o Atom e o Selector de comentários.
1 2 |
touch src/stores/atoms/commentsAtom.js touch src/stores/selectors/commentsCountSelector.js |
3 – No Atom que criamos coloque:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { atom } from 'recoil'; const commentsState = atom({ key: 'commentsState', default: [ { username: "Leonardo", text: "Este comentário vem direto do Atom!" } ], }); export default commentsState; |
Detalhes:
-
Vamos usar este Atom (estado) para salvar o array de comentários da nossa aplicação
-
Key é o nome único que damos a este estado (no caso commentsState) e default é o valor que ele vai iniciar (no caso um comentário)
4 – Atualize o component Comments para exibir os comentários apartir do Atom:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import React, {Fragment} from 'react'; import Comment from './Comment'; import Form from './Form'; import { useRecoilState } from 'recoil'; import commentsState from '../../stores/atoms/CommentsAtom'; function Comments() { const [comments, setComments] = useRecoilState(commentsState); return ( <Fragment> <div> <h2>3 Comentários</h2> <hr /> {comments.map((comment, index) => { return <Comment username={comment.username} text={comment.text} key={index}/> })} </div> <div> <Form /> </div> </Fragment> ); } export default Comments; |
Detalhes
-
Primeiro importamos o hook useRecoilState para podermos usar o Recoil no nosso component
-
Depois importamos o nosso Atom criado anteriomente
-
Depois usamos o Hook passando nosso Atom commentsState (em: const [comments, setComments] = useRecoilState(commentsState)) para criar uma constante com os comentários (comments) e uma função para alterar o Atom (setComments)
-
Finalmente para usar os commentários que vem do Atom nós criamos um looping (comments.map…) que pega cada comentário de comments e exibe usando o component Comment
5 – Para ver o resultado suba o servidor:
Npm
1 |
npm start |
Yarn
1 |
yarn start |
Criando e usando o Selector
1 – Agora vamos criar um selector que conta o número de comentários, no selector que criamos coloque:
1 2 3 4 5 6 7 8 9 10 11 12 |
import { selector } from 'recoil'; import commentsState from '../atoms/CommentsAtom'; const commentsCountState = selector({ key: 'commentsCountState', get: ({get}) => { const comments = get(commentsState); return comments.length; } }); export default commentsCountState; |
Detalhes
-
Primeiro importamos o selector da biblioteca recoil para podermos criar nosso próprio selector
-
Depois importamos nosso state Comments para poder utiliza-lo na hora de contar os comentários (selectors podem receber Atoms ou outros Selectors e eles são atualizados assim que suas dependencias mudam)
-
Declaramos nosso selector passando uma key que é a chave única que identifica aquele selector (no caso commentsCountState) e depois passando um get que recebe uma função que executa o processamento para dar o resultado desejado (no caso contar o numero de comentários do array).
-
Dentro da função nós também chamamos um get passando o Atom commentsState, ele é responsável por pegar o valor do Atom para o processamento e também por conectar o selector ao Atom fazendo com que o Selector mude quando o Atom mudar.
2 – No nosso component Comments vamos incluir a contagem de comentários colocando:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import React, { Fragment } from 'react'; import Comment from './Comment'; import Form from './Form'; import { useRecoilState, useRecoilValue } from 'recoil'; import commentsState from '../../stores/atoms/CommentsAtom'; import CommentCountState from '../../stores/selectors/CommentCountSelector'; function Comments() { const [comments, setComments] = useRecoilState(commentsState); const commentCount = useRecoilValue(CommentCountState); return ( <Fragment> <div> <h2>{commentCount} Comentários</h2> <hr /> {comments.map((comment, index) => { return <Comment username={comment.username} text={comment.text} key={index} /> })} </div> <div> <Form /> </div> </Fragment> ); } export default Comments; |
Detalhes
-
Primeiro importamos o useRecoilValue para poder usar nosso selector
-
Depois importamos nosso selector
-
Declaramos nosso selector e colocamos o resultado dele em uma constante (const commentCount = useRecoilValue(CommentCountState))
-
Exibimos o número de comentários chamando {commentCount}
3 – Para ver o resultado suba o servidor:
Npm
1 |
npm start |
Yarn
1 |
yarn start |
Atualizando o valor do Atom
1 – Para podermos adicionar comentários ao Atom nós precisamos passar a função setComments para o Component Form, então em Comments atualize:
De:
1 |
<Form /> |
Para:
1 |
<Form setComments={setComments} comments={comments}/> |
2 – Vamos atualizar o nosso component Form para que ele se transforme em um component controlado (para que seus inputs estejam associados a states locais), coloque nele:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import React, { useState } from 'react'; function Form(props) { const [username, setUsername] = useState(""); const [text, setText] = useState(""); return ( <div> <form> <div> <strong>Deixe seu comentário:</strong> </div> <div> <input type="text" name="username" placeholder='Nome de usuário' value={username} onChange={e => setUsername(e.target.value)} /> </div> <div> <textarea type="text" name="text" placeholder='Seu comentário...' value={text} onChange={e => setText(e.target.value)} /> </div> <input type="submit" value="Enviar" /> </form> </div> ); } export default Form; |
Detalhes
-
Incluimos o hook de estados locais (useState)
-
Criamos os estados correspondentes ao Input (username e text)
-
Nas Tags input nós associamos seus valores aos estados e chamamos a função correspondente para que quando eles sejam atualizados eles atualizem também os estados
3 – Agora vamos criar uma função para que quando o form seja submetido ele chame o setComments que passamos anteriomente para atualizar o nosso Atom, no component Form coloque:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import React, { useState } from 'react'; function Form(props) { const [username, setUsername] = useState(""); const [text, setText] = useState(""); const handleSubmit = (evt) => { evt.preventDefault(); props.setComments([...props.comments, {username: username, text: text}]) setUsername('') setText('') } return ( <div> <form onSubmit={handleSubmit}> <div> <strong>Deixe seu comentário:</strong> </div> <div> <input type="text" name="username" placeholder='Nome de usuário' value={username} onChange={e => setUsername(e.target.value)} /> </div> <div> <textarea type="text" name="text" placeholder='Seu comentário...' value={text} onChange={e => setText(e.target.value)} /> </div> <input type="submit" value="Enviar" /> </form> </div> ); } export default Form; |
Detalhes
-
Primeiro criamos a função handleSubmit que é chamada assim que submetemos nosso form
-
Nela primeiro nós passamos o comando para impedir que a página seja atualizada assim que o form fosse submentido (evt.preventDefault();)
-
Depois nós chamamos o setComments passando os comentários anteriores (usando o operado spread …props.comments) e o novo comentário gerado através do form
-
Chamamos também as funções setUsername e setText para limpar os campos depois de enviarmos o form
-
Por fim nós colocamos o onSubmit={handleSubmit} na Tag form para que ela chamasse nossa função na hora do envio
4 – Para ver o resultado suba o servidor:
Npm
1 |
npm start |
Yarn
1 |
yarn start |
5 – Pronto, nosso exemplo foi finalizado com sucesso o/
Conclusão
Neste exemplo foi fácil perceber como é realmente prático utilizar o Recoil para gerenciar e compartilhar estados entre components no React, espero que você considere utilizar ele em seus próximos projetos.
O Recoil ainda é um trabalho em andamento (eu escrevo este artigo em Julho de 2020 :)) e ele deve receber muitas features interessantes como o controle dos side effects (que envolve a chamada da APIs para sincronizar os estados) e muito mais.
Você pode acessar o exemplo completo no Github: https://github.com/OneBitCodeBlog/recoil-react
Obrigado por ler este artigo, se gostou envie ele para um(a) amigo(a), isso nos ajuda a levar o conhecimento a mais pessoas.
Grande abraço,
Leonardo Scorza
Programador(a), uma pergunta importante!
Você sente verdadeiramente que é um Programador(a) completo(a)?
Por muito tempo eu fiquei em dúvida se eu estava pronto ou não e essa insegurança me levou a pesquisar profundamente sobre o que realmente é necessário para ser um programador web completo.
Depois de fazer muitos treinamentos, ler muitos livros, conversar com outros desenvolvedores, ir a eventos e até trabalhar em empresas do Brasil e do exterior foi que eu pude encontrar as bases fundamentais para ser um programador Web com confiança (sem medo de buscar jobs e assumir grandes projetos).
Eu peguei todo esse conhecimento e somei com toda a didática que eu aprendi criando cursos e tutoriais que são acessados por milhares de pessoas todos os meses e criei um treinamento chamado Programador Full Stack Javascript.
Nele eu vou te mostrar o ‘caminho das pedras’, ou seja, o essencial para você dominar a programação web, se sentir mais completo(a) e conseguir o seu tão merecido Job de respeito.
Então se você quer evoluir de verdade (sem perda de tempo), clica no link abaixo e conheça o treinamento agora.
Link: Programador Full Stack Javascript em 8 semanas ->
show muito bom