Tự học ReactJS: Đẩy state lên component cha (lifting state)

Một câu hỏi phổ biến mà các bạn React mới bắt đầu hay quan tâm là: “Làm thế nào để chia sẻ state giữa hai component anh em với nhau“. Câu trả lời là “đẩy state đó lên component cha” (lifting state) có nghĩa là bạn tìm một cha chung gần nhất cho hai component con đó và đặt state ở đó. Sau đó, truyền state xuống và áp dụng một số cách để state đã truyền xuống cho component con mỗi khi cần.

Lifting state

Bạn nhận được một feature mới là bạn phải hiển thị tên con vật mà người dùng nhập cho component Display. Nhưng state của con vật đang được quản lý bởi một component anh em khác. Bạn hãy tìm một component cha gần nhất để quản lí state đó và truyền props xuống cho Display component.

Preview(opens in a new tab)

import * as React from 'react'

function Name({name, onNameChange}) {
  return (
    <div>
      <label htmlFor="name">Name: </label>
      <input id="name" value={name} onChange={onNameChange} />
    </div>
  )
}

// 🐨 nhận `animal` and `onAnimalChange` props cho component này
function FavoriteAnimal() {
  // 💣 xoá state này, bây giờ nó được quản lí bởi App
  const [animal, setAnimal] = React.useState('')
  return (
    <div>
      <label htmlFor="animal">Favorite Animal: </label>
      <input
        id="animal"
        value={animal}
        onChange={event => setAnimal(event.target.value)}
      />
    </div>
  )
}

// 🐨 bỏ comment cho component này
// function Display({name, animal}) {
//   return <div>{`Hey ${name}, your favorite animal is: ${animal}!`}</div>
// }

// 💣  xoá component này
function Display({name}) {
  return <div>{`Hey ${name}, you are great!`}</div>
}

function App() {
  // 🐨 thêm useState để quản lí animal
  const [name, setName] = React.useState('')
  return (
    <form>
      <Name name={name} onNameChange={event => setName(event.target.value)} />
      {/* 🐨 truyền animal and onAnimalChange prop (similar to the Name component above) */}
      <FavoriteAnimal />
      {/* 🐨 truyền the animal prop here */}
      <Display name={name} />
    </form>
  )
}

export default App

Đáp án:

import * as React from 'react'

function Name({name, onNameChange}) {
  return (
    <div>
      <label htmlFor="name">Name: </label>
      <input id="name" value={name} onChange={onNameChange} />
    </div>
  )
}

function FavoriteAnimal({animal, onAnimalChange}) {
  return (
    <div>
      <label htmlFor="animal">Favorite Animal: </label>
      <input id="animal" value={animal} onChange={onAnimalChange} />
    </div>
  )
}

function Display({name, animal}) {
  return <div>{`Hey ${name}, your favorite animal is: ${animal}!`}</div>
}

function App() {
  const [animal, setAnimal] = React.useState('')
  const [name, setName] = React.useState('')
  return (
    <form>
      <Name name={name} onNameChange={event => setName(event.target.value)} />
      <FavoriteAnimal
        animal={animal}
        onAnimalChange={event => setAnimal(event.target.value)}
      />
      <Display name={name} animal={animal} />
    </form>
  )
}

export default App

Liên kết state và component(Collocating state)

Bây giờ bạn hiểu rõ về lifting state và khi bạn quen dần với cách này bạn sẽ nhanh chóng tổ chức state của bạn được tốt nhất trong app. Nhưng có một kỹ thuật khác, nghe có vẻ trái ngược với lifting state là pushing state back down(di chuyển state xuống dưới những component phù hợp)

Bạn nhận được một feature là trong component Display chúng ta chỉ cần hiển thị animal, không cần name prop nữa.

Component <Display /> chỉ nhận một prop như sau:

function Display({animal}) {
  return <div>{`Your favorite animal is: ${animal}!`}</div>
}

Do đó bạn phải di chuyển state của name xuống component Name.

Kết quả như thế này:

import * as React from 'react'

function Name() {
  const [name, setName] = React.useState('')
  return (
    <div>
      <label htmlFor="name">Name: </label>
      <input
        id="name"
        value={name}
        onChange={event => setName(event.target.value)}
      />
    </div>
  )
}

function FavoriteAnimal({animal, onAnimalChange}) {
  return (
    <div>
      <label htmlFor="animal">Favorite Animal: </label>
      <input id="animal" value={animal} onChange={onAnimalChange} />
    </div>
  )
}

function Display({animal}) {
  return <div>{`Your favorite animal is: ${animal}!`}</div>
}

function App() {
  const [animal, setAnimal] = React.useState('')
  return (
    <form>
      <Name />
      <FavoriteAnimal
        animal={animal}
        onAnimalChange={event => setAnimal(event.target.value)}
      />
      <Display animal={animal} />
    </form>
  )
}

export default App

thaunguyen.com via Epic React by Kent C.Dodds

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *