Alterando o estado do componente pai em um componente filho no React
Na lição anterior, o estado com os dados era armazenado no componente pai, e os componentes filhos recebiam esses dados via props.
Agora, suponha que queiramos alterar nossos produtos.
Vamos criar, por exemplo, um botão que irá
adicionar nosso produto ao carrinho. Para começar,
vamos adicionar ao nosso array de produtos
um campo inCart, indicando se o produto
está no carrinho ou não:
const initProds = [
{id: id(), name: 'product1', cost: 100, inCart: false},
{id: id(), name: 'product2', cost: 200, inCart: false},
{id: id(), name: 'product3', cost: 300, inCart: false},
];
No componente Products, na tag do produto,
vamos adicionar mais um atributo inCart:
function Products() {
const [prods, setProds] = useState(initProds);
const items = prods.map(prod => {
return <Product
key ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart={prod.inCart}
/>;
});
return <div>
{items}
</div>;
}
Vamos no componente filho Product
fazer a exibição da informação sobre o carrinho e um botão
para adicionar ao carrinho:
function Product({ id, name, cost, inCart }) {
return <div>
name: <span>{name}</span>,
cost: <span>{cost}</span>,
<span>{inCart ? 'no carrinho' : 'não está no carrinho'}</span>
<button>para o carrinho</button>
</div>;
}
Vamos implementar a adição
De acordo com as regras do React, um componente não deve
alterar suas próprias props. Isso significa que
um componente filho não pode se adicionar
ao carrinho alterando a prop inCart.
Isso não está correto.
A maneira correta é pedir ao componente pai
que altere seu estado prods, colocando
um determinado produto no carrinho.
Vamos ver como isso é feito.
No componente pai, vamos criar uma função addToCart,
que recebe como parâmetro o id do produto
e para esse produto altera a propriedade inCart
para true:
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
Na tag do produto, vamos adicionar um atributo no qual
passaremos a função que criamos, e também
um atributo no qual passaremos o id do produto:
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
Como você pode ver, nas props dos componentes é possível passar não apenas alguns dados, mas também funções.
O código final do componente Products ficará
da seguinte forma:
function Products() {
const [prods, setProds] = useState(initProds);
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
return <div>
{items}
</div>;
}
Agora, no componente filho, teremos acesso à
função addToCart. Vamos chamar essa função
no clique do botão, passando para ela como parâmetro
o id do produto:
function Product({ id, name, cost, inCart, addToCart }) {
return <div>
name: <span>{name}</span>,
cost: <span>{cost}</span>,
<span>{inCart ? 'no carrinho' : 'não está no carrinho'}</span>
<button onClick={() => addToCart(id)}>para o carrinho</button>
</div>;
}
O resultado é que, ao clicar no botão no componente filho, a função do pai será chamada, e esta irá alterar o estado do pai. A alteração do estado do pai irá acionar uma nova renderização e redesenhará nosso produto, passando para ele a prop alterada.
Pegue o componente User da lição
anterior. Faça com que apareça nele um
botão para banir o usuário.