Arbetslägen via state i underkomponenter i React

Låt vår produktarray nu se ut så här:

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

Låt oss visa dessa produkter som en HTML tabell. Låt oss också göra så att när man klickar på vilken cell som helst i tabellen visas en inmatningsruta för redigering i den cellen. För att lösa uppgiften kommer vi att skapa 3 komponenter.

Komponenten Products kommer att lagra state med produkter och använda komponenter Product för att visa produkter. Komponenten Product kommer i sin tur också att använda komponenter ProductField för att visa ett specifikt produktfält (namn, pris, kategori).

Komponenten ProductField kommer antingen visa fältets text, eller en inmatningsruta för att redigera den. Samtidigt låter vi redigeringsläget eller visningen regleras av denna komponents state.

Det vill säga vi kommer inte att lagra läget i föräldrakomponentens state. Det skulle vara väldigt obekvämt där, eftersom vi skulle behöva ange läge för varje produktfält, vilket skulle förvandla vårt state till något liknande:

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

Vi kommer dock inte att skapa ett sådant state, utan behåller det som fanns. Varje instans av komponenten ProductField kommer med hjälp av sitt eget state reglera läget: antingen redigering eller visning.

Således kommer föräldrakomponenten att lagra state med data, och vår barnbarnskomponent kommer att få dessa data via props och samtidigt ha ett eget state för att ändra sitt läge.

Så, låt oss implementera det som beskrivits.

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

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

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

Praktiska uppgifter

Задание 1

Utför liknande operationer med komponenterna Users och User som du skapade i tidigare lektioner.