Modos de trabajo a través de estados en componentes hijos en React

Supongamos que nuestro array de productos ahora luce de la siguiente manera:

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

Vamos a mostrar estos productos en forma de tabla HTML. Al mismo tiempo, hagamos que al hacer clic en cualquier celda de la tabla aparezca un input para editar. Para resolver la tarea crearemos 3 componentes.

El componente Products almacenará el estado con los productos y utilizará componentes Product para mostrar los productos. El componente Product a su vez también utilizará componentes ProductField para mostrar un campo específico del producto (nombre, precio, categoría).

El componente ProductField mostrará ya sea el texto del campo, o un input para editarlo. Al mismo tiempo, que el modo de edición o visualización se regule mediante el estado de este componente.

Es decir, no almacenaremos el modo en el estado padre. Allí sería muy inconveniente, ya que tendríamos que especificar el modo para cada campo del producto, lo que convertiría nuestro estado en algo similar a esto:

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

Sin embargo, no crearemos dicho estado, sino que mantendremos el que teníamos. Simplemente cada instancia del componente ProductField mediante su propio estado regulará el modo: edición o visualización.

De este modo, resultará que el componente padre almacenará el estado con los datos, y nuestro componente nieto recibirá estos datos a través de props y al mismo tiempo tendrá su propio estado para cambiar su modo.

Entonces, implementemos lo descrito.

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

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

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

Tareas prácticas

Задание 1

Realice operaciones similares con los componentes Users y User, creados por usted en lecciones anteriores.