Chỉnh sửa state của component cha trong component con trong React

Bây giờ hãy chỉnh sửa sản phẩm của chúng ta bằng các ô nhập liệu. Để làm điều này, trong component con chúng ta sẽ tạo một nút.

Khi nhấp lần đầu vào nút này, thay vì tên và giá của sản phẩm, hãy hiển thị các ô nhập liệu để chỉnh sửa chúng, và khi nhấp lần thứ hai thay vì các ô nhập liệu, lại hiển thị văn bản.

Chúng ta sẽ thay đổi mảng sản phẩm, thêm vào thuộc tính isEdit (và để đơn giản, chúng ta sẽ bỏ phần làm việc với giỏ hàng):

jsxconst initProds = [
  {id: id(), name: 'product1', cost: 100, isEdit: false},
  {id: id(), name: 'product2', cost: 200, isEdit: false},
  {id: id(), name: 'product3', cost: 300, isEdit: false},
];

Component Product

Hãy tạo một nút để chỉnh sửa trong sản phẩm:

jsxfunction Product({ id, name, cost, isEdit }) {
  return <div>
    name: <span>{name}</span>
    cost: <span>{cost}</span>
    <button>edit</button>
  </div>;
}

Hãy làm sao cho khi nhấp vào nút này một hàm nào đó toggleMode sẽ được gọi, hàm này được truyền từ component cha:

jsxfunction Product({ id, name, cost, isEdit, toggleMode }) {
  return <div>
    name: <span>{name}</span>
    cost: <span>{cost}</span>
    
    <button onClick={() => toggleMode(id)}>
      edit
    </button>
  </div>;
}

Hiện tại chúng ta chưa có triển khai toggleMode, nhưng chúng ta biết rằng nó sẽ nằm trong component cha, nhận tham số là id của sản phẩm và thay đổi thuộc tính isEdit của sản phẩm thành giá trị ngược lại.

Chúng ta cũng sẽ làm sao cho văn bản của nút thay đổi sau mỗi lần nhấp:

jsxfunction Product({ id, name, cost, isEdit, toggleMode }) {
  return <div>
    name: <span>{name}</span>
    cost: <span>{cost}</span>
    
    <button onClick={() => toggleMode(id)}>
      {isEdit ? 'save': 'edit'}
    </button>
  </div>;
}

Bây giờ hãy làm sao cho trong chế độ chỉnh sửa, chúng ta có các ô nhập liệu với dữ liệu, còn trong chế độ thông thường - các thẻ span:

jsxfunction Product({ id, name, cost, isEdit, toggleMode }) {
  return <div>
    name: {isEdit ? <input value={name} /> : <span>{name}</span>}
    cost: {isEdit ? <input value={cost} /> : <span>{cost}</span>}
    
    <button onClick={() => toggleMode(id)}>
      {isEdit ? 'save': 'edit'}
    </button>
  </div>;
}

Hãy liên kết sự kiện onChange với các ô nhập liệu của chúng ta, trong đó chúng ta sẽ gọi một hàm nào đó của component cha là editProd:

jsxfunction Product({ id, name, cost, isEdit, toggleMode, editProd }) {
  return <div>
    name: {
      isEdit
      ? <input value={name} onChange={event => editProd(id, 'name', event)} />
      : <span>{ name }</span>
    }
    cost: {
      isEdit
      ? <input value={cost} onChange={event => editProd(id, 'cost', event)} />
      : <span>{ cost }</span>
    }
    
    <button onClick={() => toggleMode(id)}>
      {isEdit ? 'save': 'edit'}
    </button>
  </div>;
}

Component Products

Bây giờ hãy chuyển sang component Products. Chúng ta sẽ triển khai hàm toggleMode trong đó:

jsxfunction toggleMode(id) {
  setProds(prods.map(prod => {
    if (prod.id === id) {
      prod.isEdit = !prod.isEdit;
    }
    
    return prod;
  }));
}

Chúng ta cũng sẽ triển khai hàm editProd trong đó:

jsxfunction editProd(id, field, event) {
  setProds(prods.map(prod => {
    if (prod.id === id) {
      prod[field] = event.target.value;
    }
    
    return prod;
  }));
}

Chúng ta sẽ truyền các hàm toggleMode và editProd của chúng ta dưới dạng thuộc tính vào thẻ sản phẩm:

jsxconst items = prods.map(prod => {
  return <Product
    key ={prod.id}
    id  ={prod.id}
    name={prod.name}
    cost={prod.cost}
    isEdit={prod.isEdit}
    toggleMode={toggleMode}
    editProd={editProd}
  />;
});

Mã cuối cùng của component Products sẽ như sau:

jsxfunction Products() {
  const [prods, setProds] = useState(initProds);
  
  function toggleMode(id) {
    setProds(prods.map(prod => {
      if (prod.id === id) {
        prod.isEdit = !prod.isEdit;
      }
      
      return prod;
    }));
  }
  
  function editProd(id, field, event) {
    setProds(prods.map(prod => {
      if (prod.id === id) {
        prod[field] = event.target.value;
      }
      
      return prod;
    }));
  }
  
  const result = prods.map(prod => {
    return <Product
      key ={prod.id}
      id  ={prod.id}
      name={prod.name}
      cost={prod.cost}
      isEdit={prod.isEdit}
      toggleMode={toggleMode}
      editProd={editProd}
    />;
  });
  
  return <div>
    {result}
  </div>;
}

Bài tập thực hành

Задание 1

Hãy thực hiện các thao tác tương tự với các component Users và User mà bạn đã tạo trong các bài học trước.