Tryby pracy przez stany komponentów potomnych w React

Załóżmy, że nasza tablica z produktami wygląda teraz następująco:

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'},
];

Wyświetlmy te produkty w postaci tabeli HTML. Jednocześnie zróbmy tak, aby po kliknięciu na dowolną komórkę tabeli w tej komórce pojawiło się pole wprowadzania do edycji. Aby rozwiązać zadanie stworzymy 3 komponenty.

Komponent Products będzie przechowywać stan z produktami i używać komponentów Product do wyświetlania produktów. Komponent Product z kolei również będzie używać komponentów ProductField do wyświetlania określonego pola produktu (nazwy, ceny, kategorii).

Komponent ProductField będzie albo wyświetlać tekst pola, albo pole wprowadzania do jego edycji. Przy tym tryb edycji lub wyświetlania niech reguluje się stanem tego komponentu.

Czyli nie będziemy przechowywać trybu w stanie rodzica. Byłoby to bardzo niewygodne, ponieważ musielibyśmy określać tryb dla każdego pola produktu, co przekształciłoby nasz stan w coś podobnego do:

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 jednak nie zrobimy takiego stanu, a pozostawimy ten, który był. Po prostu każda instancja komponentu ProductField za pomocą swojego stanu będzie regulować tryb: albo edycję, albo wyświetlanie.

W ten sposób okaże się, że komponent rodzica będzie przechowywać stan z danymi, a nasz komponent wnuka będzie otrzymywać te dane przez właściwości i przy tym będzie miał swój stan do zmiany swojego trybu.

A więc, zrealizujmy opisane.

Komponent 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>;
}

Komponent 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>;
}

Komponent 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>;
}

Zadania praktyczne

Задание 1

Wykonaj podobne operacje z komponentami Users i User, stworzonymi przez ciebie w poprzednich lekcjach.