State lifting
Sdílení dat a komunikace mezi komponentami je jedna z největších výzev v architektuře Reactových aplikací. V praxi budeme často v situaci, kdy nějaká komponenta potřeboje data zevnitř některého ze svých potomků. React však nemá žádný nástroj nebo příkaz, jakým by předek mohl z potomka získat data. Tuto situaci musíme vyřešit pomocí techniky s názvem state liftingpovýšení stavu.
Ukázka: Hračkorama
K ilustraci výše popsané situace použijeme project e-shopu Hračkorama. Základ projektu najdete v tomto repozitáři.
V tuto chvíli nás bude zajímat komponenta Amount
. Ta umožňuje zvolit počet kusů daného produktu. V komponentě Cart
budeme chtít zobrazovat celkový počet položek v košíku. Bohužel tato komponenta se nemá jak dostat k číslu ve stavu komponenty Amount
. Budeme tedy muset provést povýšení stavu.
Nejprve z cvičných důvodů povýšíme stav do kompnenty CartItem
. To sice náš problém ještě nevyřeší, ale ukázeme si tím základní principy.
Stav count
nyní bude v komponentě CartItem
.
interface ICartItemProps {
product: IProduct;
}
const CartItem: React.FC<ICartItemProps> = ({ product }) => {
return (
<div className="cart-item">
<CartProduct name={product.name} price={product.price} />
<Amount value={product.amount} />
</div>
)
};
Stav pak předáme pomocí prop value
do komponenty Amount
. Nyní potřebujeme, aby komponenta Amount
dala vědět o změně své hodnoty.
const CartItem: React.FC<ICartItemProps> = ({ product }) => {
const [count, setCount] = useState<number>(product.amount);
const handleAmountChange = (newCount) => {
setCount(newCount);
}
return (
<div className="cart-item">
<CartProduct name={product.name} price={product.price} />
<Amount value={count} onChange={handleAmountChange} />
</div>
)
};
Komponenta Amount
pak bude vypadat takto:
interface IAmountProps {
value: number;
onChange: (newCount: number) => void;
}
const Amount: React.FC<IAmountProps> = ({ value, onChange }) => {
const handelIncrement = () => {
onChange(value + 1);
}
const handelDecrement = () => {
if (count > 0) {
onChange(value - 1);
}
}
return (
<div className="amount">
<button className="amount__btn" onClick={handelDecrement}>–</button>
<div className="amount__count">{value}</div>
<button className="amount__btn" onClick={handelIncrement}>+</button>
</div>
);
};
Chování aplikace se tímto nezměnilo, stav jsme však povýšili o jednu úroveň výše.