04 de março de 2022 • 8 min de leitura
Context API
Como usar Context API
Antes de apresentar a solução que a Context API resolve, vou apresentar o problema.
Para que você entenda perfeitamente o problema, vamos criar alguns componentes, no seu arquivo App.js faça:
import { useEffect, useState } from "react";
import Navbar from "./components/Navbar";
import Header from "./components/Header";
import Card from "./components/Card";
function App() {
const [user, setUser] = useState();
useEffect(() => {
setUser("Bruno");
}, []);
return (
<div className="App">
<Navbar user={user} />
<main>
<Header user={user} />
<Card user={user} />
</main>
</div>
);
}
export default App;
No src crie a pasta components, dentro de components crie a pasta Card, dentro de Card crie index.jsx com:
import CardImage from "../CardImage";
import CardTitle from "../CardTitle";
import CardBody from "../CardBody";
export default function Card({ user }) {
return (
<div className="Card">
<CardImage />
<CardTitle user={user} />
<CardBody />
<button>Botão</button>
</div>
);
}
Dentro de components crie a pasta CardBody com o arquivo index.jsx, no arquivo coloque:
function CardBody() {
return (
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
);
}
export default CardBody;
Dentro de components crie a pasta CardImage com o arquivo index.jsx, no arquivo coloque:
function CardImage() {
return <div className="img" />;
}
export default CardImage;
Dentro de components crie a pasta CardTitle com o arquivo index.jsx, no arquivo coloque:
function CardTitle({ user }) {
return <h2>{user}</h2>;
}
export default CardTitle;
Dentro de components crie a pasta Header com o arquivo index.jsx, no arquivo coloque:
function Header({ user }) {
return (
<div className="Header">
<h1>Usuário: {user}</h1>
</div>
);
}
export default Header;
Dentro de components crie a pasta Navbar com o arquivo index.jsx, no arquivo coloque:
function Navbar({ user }) {
return (
<ul className="horizontal">
<li>
<a href="home">Home</a>
</li>
<li>
<a href="news">News</a>
</li>
<li>
<a href="contact">Contact</a>
</li>
<li className="rightli floatright">
<a href="user" className="active">
Olá, {user}
</a>
</li>
</ul>
);
}
export default Navbar;
Joia! Agora pra finalizar coloque esse CSS no arquivo index.css principal do projeto:
body {
margin: 0;
padding: 0;
}
.App {
margin: 0;
}
ul.horizontal {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: rgb(51 51 51);
}
ul {
display: block;
list-style-type: disc;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
padding-inline-start: 40px;
}
ul.horizontal li {
float: left;
}
ul.horizontal li a.active {
background-color: rgb(4 170 109);
}
ul.horizontal li a {
display: inline-block;
color: rgb(255 255 255);
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.floatright {
float: right !important;
}
main {
padding: 32px 64px;
}
.Card {
padding: 5%;
border-width: 1px;
border-style: solid;
border-radius: 10px;
box-shadow: 10px 10px 10px -10px black;
margin-top: 3rem;
height: auto;
width: 260px;
}
.Card button {
width: 100%;
border-radius: 10px;
height: 2rem;
font-size: 1rem;
box-shadow: 7px 7px 10px -10px black;
font-weight: 700;
border: none;
}
.Card .img {
width: 100%;
height: 150px;
opacity: 0.9;
background: url("https://assets-global.website-files.com/5d9bc5d562ffc2869b470941/5e1f8bd1dc3c511ea5a28a56_icon-rect-tech.png");
background-size: cover;
}
Legal! Feito tudo isso, agora execute e vamos ver o resultado:
Preste atenção em uma coisa, no App.js estou fazendo um setUser dentro do useEffect pra imitar uma chama a API, o nome Bruno que esta no state esta sendo apresentado em 3 componentes diferentes, na NavBar, no Header e no Card, o que é muito normal, as vezes temos o mesmo dado sendo apresentado em diversos componentes.
O problema esta em como passar esses dados de um lado para o outro, passando props pra lá e pra cá, passando props até para componentes que não vão precisar desse dado somente para passar para outro componente filho, esse problema se chama Prop Drilling, veja o exemplo como estamos passando a prop para o component Card somente repassar para o filho CardTitle:
<div className="App">
<Navbar user={user} />
<main>
<Header user={user} />
<Card user={user} />
</main>
</div>
import CardImage from "../CardImage";
import CardTitle from "../CardTitle";
import CardBody from "../CardBody";
export default function Card({ user }) {
return (
<div className="Card">
<CardImage />
<CardTitle user={user} />
<CardBody />
<button>Botão</button>
</div>
);
}
function CardTitle({ user }) {
return <h2>{user}</h2>;
}
export default CardTitle;
Não seria mais fácil se tivéssemos esse dado em algum outro lugar e importar somente no componente que precisa desse dado? Sim! Pois é, esse é o problema que Context API pode te ajudar a resolver! Vamos começar a implementação.
No src crie uma pasta context, dentro crie o arquivo user.js com o código:
import { useState, createContext } from "react";
export const UserContext = createContext();
export const UserProvider = (props) => {
const [user, setUser] = useState("Bruno");
return (
<UserContext.Provider value={{ user, setUser }}>
{props.children}
</UserContext.Provider>
);
};
Vou explicar as coisas referentes a Context API, o restante provavelmente você já sabe. Estamos importando createContext que nos ajuda na criação do context, criando uma const UserContext que recebe a função createContext do React, criando o UserProvider que é o nosso provedor das informações referentes a usuário, criando o state que armazena as informações, no return temos a declaração do provedor, value são as informações que queremos que o provedor disponibilize e children são as páginas do nosso projeto.
Legal, agora precisamos importar nosso provedor lá no index.js principal:
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { UserProvider } from "./context/user";
import "./index.css";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<UserProvider>
<App />
</UserProvider>
</StrictMode>,
rootElement
);
Como o componente App agora é filho do nosso UserProvider, toda essa página ou componente juntamente com seus filhos tem acesso as informações disponibilizadas por esse provedor.
Agora já podemos dar uma limpada no nosso App.js e parar de passar as props:
import Navbar from "./components/Navbar";
import Header from "./components/Header";
import Card from "./components/Card";
function App() {
return (
<div className="App">
<Navbar />
<main>
<Header />
<Card />
</main>
</div>
);
}
export default App;
Joia, agora já podemos usar nosso context pegando dados dele, vamos começar lá pela nossa Navbar, vai ficar dessa forma:
import { useContext } from "react";
import { UserContext } from "../../context/user";
function Navbar() {
const { user } = useContext(UserContext);
return (
<ul className="horizontal">
<li>
<a href="home">Home</a>
</li>
<li>
<a href="news">News</a>
</li>
<li>
<a href="contact">Contact</a>
</li>
<li className="rightli floatright">
<a href="user" className="active">
Olá, {user}
</a>
</li>
</ul>
);
}
export default Navbar;
Viu? O nome voltou a ser apresentado na Navbar!
Aqui nos estamos importante a função useContext do React que é a função que nos auxilia no uso de contextos. Importanto nosso UserContext. Extraindo o state user de dentro do context UserContext.
Agora não estamos criando state no App e repassando para vários componentes criando Prop Drilling, o código fica mais limpo e podemos pegar informações do context somente nos componentes que precisarmos.
Vou deixar 2 desafios para você:
- Utilize o state user do context nos outros componentes que precisam dessa informação.
- Percebeu que em nosso UserContext também estamos disponibilizando a função setUser? Crie um useEffect no componente que quiser, use a função setUser para trocar o nome do usuário depois de 2 segundos. Dica: use a função setTimeOut.