Režimy práce přes stavy komponent-potomků v Reactu
Nechť naše pole s produkty nyní vypadá následovně:
const initProds = [
{id: id(), name: 'prod1', cost: 'cost1', catg: 'catg1'},
{id: id(), name: 'prod2', cost: 'cost2', catg: 'catg2'},
{id: id(), name: 'prod3', cost: 'cost3', catg: 'catg3'},
];
Vypišme tyto produkty ve formě HTML
tabulky. Zároveň udělejme to, aby po kliknutí
na libovolnou buňku tabulky se v této buňce objevilo
vstupní pole pro editaci. Pro řešení úlohy
vytvoříme 3 komponenty.
Komponenta Products bude uchovávat stav
s produkty a používat komponenty Product
pro výpis produktů. Komponenta Product
bude naopak také používat komponenty
ProductField pro výpis konkrétního
pole produktu (názvu, ceny, kategorie).
Komponenta ProductField bude buď
zobrazovat text pole, nebo vstupní pole pro jeho
editaci. Přitom režim editace
nebo zobrazení nechť je řízen stavem
této komponenty.
To znamená, že režim nebudeme uchovávat v nadřazeném stavu. Tam by to bylo velmi nepohodlné, protože bychom museli uvádět režim pro každé pole produktu, což by proměnilo náš stav v něco podobného:
const initProds = [
[
{field: 'name', value: 'prod1', isEdit: false},
{field: 'cost', value: 'cost1', isEdit: false},
{field: 'catg', value: 'catg1', isEdit: false},
],
[
{field: 'name', value: 'prod2', isEdit: false},
{field: 'cost', value: 'cost2', isEdit: false},
{field: 'catg', value: 'catg2', isEdit: false},
],
[
{field: 'name', value: 'prod3', isEdit: false},
{field: 'cost', value: 'cost3', isEdit: false},
{field: 'catg', value: 'catg3', isEdit: false},
],
]
My však takový stav dělat nebudeme,
a ponecháme ten, který byl. Jednoduše každá
instance komponenty ProductField
pomocí svého stavu bude řídit
režim: buď editaci, nebo zobrazení.
Tak se stane, že nadřazená komponenta bude uchovávat stav s daty, a naše vnoučecí komponenta bude tato data získávat přes props a přitom bude mít svůj stav pro změnu svého režimu.
Takže, pojďme realizovat popsané.
Komponenta Products
function Products() {
const [prods, setProds] = useState(initProds);
function changeField(id, field, event) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod[field] = event.target.value;
}
return prod;
}));
}
const rows = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name={prod.name}
cost={prod.cost}
catg={prod.catg}
changeField={changeField}
/>;
});
return <div>
<table>
<tbody>
{rows}
</tbody>
</table>
</div>;
}
Komponenta Product
function Product({ id, name, cost, catg, changeField }) {
return <tr>
<ProductField id={id} text={name} type="name" changeField={changeField} />
<ProductField id={id} text={cost} type="cost" changeField={changeField} />
<ProductField id={id} text={catg} type="catg" changeField={changeField} />
</tr>;
}
Komponenta ProductField
function ProductField({ id, text, type, changeField }) {
const [isEdit, setIsEdit] = useState(false);
return <td>
{
isEdit
? <input
value={text}
onChange={event => changeField(id, type, event)}
onBlur={() => setIsEdit(false)}
/>
: <span onClick={() => setIsEdit(true)}>{text}</span>
}
</td>;
}
Praktické úlohy
Proveďte analogické operace s komponentami
Users a User, vytvořenými vámi
v předchozích lekcích.