Režimy práce přes stavy komponent-potomků v Reactu

Nechť naše pole s produkty nyní vypadá následovně:

jsxconst 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:

jsxconst 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

jsxfunction 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

jsxfunction 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

jsxfunction 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

Задание 1

Proveďte analogické operace s komponentami Users a User, vytvořenými vámi v předchozích lekcích.