ການເຂົ້າຄິວຊຸດອັບເດດ state

ການຕັ້ງຄ່າຕົວແປ state ຈະເປັນຄິວການ render ອື່ນ. ແຕ່ບາງເທື່ອທ່ານອາດຕ້ອງການດຳເນີນການຫຼາຍຢ່າງກັບຄ່າກ່ອນຈະເຂົ້າຄິວການ render ຕໍ່ໄປ. ເພື່ອເຮັດສິ່ງນີ້, ມັນຈະຊ່ວຍໃຫ້ທ່ານເຂົ້າໃຈວ່າ React ອັບເດດ state ຂອງ batch ແນວໃດ.

You will learn

  • “ການ batch” ແມ່ນຫຍັງ ແລະ React ໃຊ້ແນວໃດໃນການປະມວນຜົນການອັບເດດຫຼາຍ state
  • ວິທີໃຊ້ການອັບເດດຫຼາຍຢ່າງກັບຕົວແປ state ດຽວກັນໃນແຖວ

ການອັບເດດ batch state React

ທ່ານອາດຄາດຫວັງວ່າການຄິກປຸ່ມ “+3” ຈະເພີ່ມຕົວນັບສາມຄັ້ງເພາະມັນເອີ້ນໃຊ້ setNumber(number + 1) ສາມຄັ້ງ:

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>+3</button>
    </>
  )
}

ເຖິງຢ່າງໃດກໍຕາມ, ທ່ານອາດຈະຈື່ໄດ້ວ່າສ່ວນກ່ອນໜ້າ, ຄ່າຂອງການ render ແຕ່ລະຄ່າຈະໄດ້ຮັບການແກ້ໄຂ, ດັ່ງນັ້ນຄ່າຂອງ number ພາຍໃນ event handler ຂອງການ render ໂຕທຳອິດຈະເປັນ 0 ສະເໝີ, ບໍ່ວ່າທ່ານຈະເອີ້ນໃຊ້ setNumber(1) ຈັກເທື່ອກໍຕາມ:

setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);

ແຕ່ມີອີກໜຶ່ງປັດໄຈທີ່ຫຼິ້ນບ່ອນນີ້. React ຈະລໍຖ້າຈົນກວ່າ code ທັງໝົດ ໃນ event handler ຈະເຮັດວຽກກ່ອນທີ່ຈະປະມວນຜົນການອັບເດດ state ຂອງທ່ານ ນີ້ແມ່ນສາເຫດທີ່ການ render ຊໍ້າຈະເກີດຂຶ້ນ ຫຼັງຈາກ ການເອີ້ນໃຊ້ setNumber() ທັ້ງໝົດນີ້ເທົ່ານັ້ນ.

ສິ່ງນີ້ອາດເຮັດໃຫ້ທ່ານຄິດເຖິງບໍລິກອນທີ່ຮັບອໍເດີທີ່ຮ້ານອາຫານ. ບໍລິກອນຈະບໍ່ແລ່ນໄປທີ່ຫ້ອງຄົວເມື່ອເວົ້າເຖິງອາຫານຈານທຳອິດຂອງທ່ານ! ແຕ່ຈະໃຫ້ທ່ານສັ່ງອາຫານໃຫ້ແລ້ວກ່ອນ ໃຫ້ທ່ານເຮັດການປ່ຽນແປງ ແລະ ແມ້ແຕ່ຮັບອໍເດີຈາກຄົນອື່ນທີ່ໂຕະ.

An elegant cursor at a restaurant places and order multiple times with React, playing the part of the waiter. After she calls setState() multiple times, the waiter writes down the last one she requested as her final order.

Illustrated by Rachel Lee Nabors

ເຊິ່ງຈະຊ່ວຍໃຫ້ທ່ານອັບເດດຕົວແປ state ຫຼາຍຕົວ—ແມ້ແຕ່ຈາກ component ຫຼາຍໂຕ—ໂດຍບໍ່ຕ້ອງ trigger ການ renders ຊໍ້າ. ຫຼາຍເກີນໄປ. ແຕ່ນີ້ໝາຍຄວາມວ່າ UI ຈະບໍ່ໄດ້ຮັບການອັບເດດຈົນກວ່າ ຫຼັງຈາກ event handler ຂອງທ່ານ, ແລະ code ໃດໆໃນນັ້ນກໍຈະສຳເລັດ. ພຶດທິກຳນີ້ເອີ້ນວ່າ ການ batch, ເຮັດໃຫ້ແອັບ React ຂອງທ່ານເຮັດວຽກໄວຂຶ້ນຫຼາຍ. ນອກຈາກນີ້ຍັງຫຼີກຫຼ່ຽງການຈັດການກັບການ render ແບບ “ເຄິ່ງດຽວ” ທີ່ສັບສົນເຊິ່ງມີພຽງແຕ່ຕົວແປບາງໂຕເທົ່ານັ້ນທີ່ໄດ້ຮັບການອັບເດດ.

React ຈະບໍ່ batch ລະຫວ່າງ event ຫຼາຍ event ໂດຍເຈດຕະນາເຊັ່ນ ການຄິກ—ການຄິກແຕ່ລະຄັ້ງຈະໄດ້ຮັບການຈັດການແຍກກັນ. ໝັ້ນໃຈໄດ້ວ່າ React ຈະເຮັດການ batch ສະເພາະເມື່ອໂດຍທົ່ວໄປແລ້ວປອດໄພເທົ່ານັ້ນ. ນີ້ເຮັດໃຫ້ໝັ້ນໃຈໄດ້ວ່າ, ຕົວຢ່າງ ຫາກການຄິກປຸ່ມທຳອິດປິດການໃຊ້ງານແບບຟອມ, ການຄິກຄັ້ງທີ່ສອງຈະບໍ່ສົ່ງອີກຄັ້ງ.

ການອັບເດດ state ດຽວກັນຫຼາຍຄັ້ງກ່ອນການ render ຄັ້ງຕໍ່ໄປ

ເປັນກໍລະນີການໃຊ້ງານທີ່ຜິດປົກະຕິ, ແຕ່ຖ້າທ່ານຕ້ອງການອັບເດດຕົວແປ state ດຽວກັນຫຼາຍໆຄັ້ງກ່ອນທີ່ຈະ render ຄັ້ງຕໍ່ໄປ, ແທນທີ່ຈະສົ່ງ ຄ່າ state ຕໍ່ໄປ ເຊັ່ນ setNumber(number + 1), ທ່ານສາມາດສົ່ງ ຟັງຊັ່ນ ທີ່ຄຳນວນ state ຕໍ່ໄປຈາກ state ກ່ອນໜ້າໃນຄິວເຊັ່ນ setNumber(n => n + 1). ເປັນວິທີທີ່ຈະບອກ React ໃຫ້ “ເຮັດບາງຢ່າງກັບຄ່າ state” ແທນທີ່ຈະແທນມັນ.

ລອງເພີ່ມຕົວນັບທັນທີ:

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(n => n + 1);
        setNumber(n => n + 1);
        setNumber(n => n + 1);
      }}>+3</button>
    </>
  )
}

ຕອນນີ້, n => n + 1 ເອີ້ນວ່າ ຟັງຊັ່ນ updater. ເມື່ອທ່ານສົ່ງຕໍ່ໄປຍັງ state setter:

  1. React ຈັດຄິວຟັງຊັ່ນນີ້ໃຫ້ປະມວນຜົນຫຼັງຈາກທີ່ code ອື່ນໆທັງໝົດໃນ event handler ເຮັດວຽກແລ້ວ.
  2. ໃນລະຫວ່າງການ render ຄັ້ງຕໍ່ໄປ, React ຈະຜ່ານຄິວ ແລະ ແຈ້ງ state ທີ່ອັບເດດສຸດທ້າຍໃຫ້ທ່ານຮູ້.
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);

ນີ້ແມ່ນວິທີທີ່ Reacct ເຮັດວຽກຜ່ານແຖວ code ເຫຼົ່ານີ້ໃນຂະນະເອີ້ນໃຊ້ງານ event handler:

  1. setNumber(n => n + 1): n => n + 1 ແມ່ນຟັງຊັ່ນ. React ເພີ່ມລົງໃນຄິວ.
  2. setNumber(n => n + 1): n => n + 1 ແມ່ນຟັງຊັ່ນ. React ເພີ່ມລົງໃນຄິວ.
  3. setNumber(n => n + 1): n => n + 1 ແມ່ນຟັງຊັ່ນ. React ເພີ່ມລົງໃນຄິວ.

ເມື່ອທ່ານເອີ້ນໃຊ້ useState ລະຫວ່າງການ render ຕໍ່ໄປ, React ຈະຕ້ອງຜ່ານຄິວ. State number ກ່ອນໜ້ານີ້ແມ່ນ 0, ສະນັ້ນ, ນັ້ນແມ່ນສິ່ງທີ່ React ສົ່ງຜ່ານໄປຍັງຟັງຊັ່ນ updater ທຳອິດເປັນ argument n. ຈາກນັ້ນ React ຈະນຳຄ່າ return ຂອງຟັງຊັ່ນ updater ກ່ອນໜ້າຂອງທ່ານ ແລະ ສົ່ງຕໍ່ໄປຍັງ updater ໂຕຖັດໄປເປັນ n ແລະ ອື່ນໆ:

ຄິວການອັບເດດnreturns
n => n + 100 + 1 = 1
n => n + 111 + 1 = 2
n => n + 122 + 1 = 3

React ເກັບ 3 ເປັນຜົນລັບສຸດທ້າຍ ແລະ return ຈາກ useState.

ນີ້ເປັນສາເຫດທີ່ການຄິກ “+3” ໃນຕົວຢ່າງດ້ານເທິງຈະເພີ່ມຄ່າທີ່ຖືກຕ້ອງເທື່ອລະ 3.

ຈະເກີດຫຍັງຂຶ້ນຖ້າທ່ານອັບເດດ state ຫຼັງຈາກແທນທີ່ມັນ

ແລ້ວ event handler ນີ້ເດ? ທ່ານຄິດວ່າ number ຈະເປັນຫຍັງໃນການ render ຕໍ່ໄປ?

<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
      }}>Increase the number</button>
    </>
  )
}

ນີ້ແມ່ນສິ່ງທີ່ event handler ບອກໃຫ້ React ເຮັດ:

  1. setNumber(number + 5): number ເປັນ 0, ສະນັ້ນ setNumber(0 + 5). React ເພີ່ມ “ແທນຄ່າດ້ວຍ 5 ໃນຄິວຂອງມັນ.
  2. setNumber(n => n + 1): n => n + 1 ແມ່ນຟັງຊັ່ນ updater. React ເພີ່ມ ຟັງຊັ່ນນັ້ນ ໃນຄິວຂອງມັນ.

ໃນລະຫວ່າງການ render ຕໍ່ໄປ, React ຈະຕ້ອງຜ່ານຄິວ state:

ຄິວອັບເດດnreturns
“replace with 50 (unused)5
n => n + 155 + 1 = 6

React ເກັບ 6 ເປັນຜົນລັບສຸດທ້າຍ ແລະ return ຈາກ useState.

Note

ທ່ານອາດສັງເກດເຫັນວ່າ setState(5) ໃຊ້ງານໄດ້ແທ້ຄືກັບ setState(n => 5) ແຕ່ n ບໍ່ຖືກໃຊ້ງານ!

ຈະເກີດຫຍັງຂຶ້ນຖ້າທ່ານແທນທີ່ state ຫຼັງຈາກອັບເດດມັນ

ລອງອີກຕົວຢ່າງໜຶ່ງ. ທ່ານຄິດວ່າ number ຈະເປັນຫຍັງໃນການ render ຄັ້ງຕໍ່ໄປ?

<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
        setNumber(42);
      }}>Increase the number</button>
    </>
  )
}

ນີ້ແມ່ນວິທີທີ່ React ເຮັດວຽກຜ່ານແຖວຂອງ code ເຫຼົ່ານີ້ໃນຂະນະທີ່ເອີ້ນໃຊ້ງານ event handler ນີ້:

  1. setNumber(number + 5): number ເປັນ 0, ສະນັ້ນ setNumber(0 + 5). React ເພີ່ມ “ແທນທີ່ດ້ວຍ 5 ໃສ່ຄິວຂອງມັນ.
  2. setNumber(n => n + 1): n => n + 1 ເປັນຟັງຊັ່ນ updater. React ເພີ່ມ ຟັງຊັ່ນນັ້ນ ໃສ່ຄິວຂອງມັນ.
  3. setNumber(42): React ເພີ່ມ “ແທນທີ່ດ້ວຍ 42 ໃສ່ຄິວຂອງມັນ.

ໃນລະຫວ່າງການ render ຕໍ່ໄປ, React ຈະຕ້ອງຜ່ານຄິວ state:

ຄິວອັບເດດnreturns
“replace with 50 (unused)5
n => n + 155 + 1 = 6
“replace with 426 (unused)42

ຈາກນັ້ນ React ຈະເກັບ 42 ເປັນຜົນລັບສຸດທ້າຍ ແລະ return ຈາກ useState.

ສະຫຼຸບ, ຕໍ່ໄປນີ້ແມ່ນວິທີທີ່ຄິດຂອງສິ່ງທີ່ທ່ານສົ່ງຜ່ານໄປຍັງ state setter setNumber:

  • ຟັງຊັ່ນ updater (ເຊັ່ນ n => n + 1) ຈະຖືກເພີ່ມລົງໃນຄິວ.
  • ຄ່າອື່ນໆ (ເຊັ່ນ number 5) ເພີ່ມ “ແທນທີ່ດ້ວຍ 5” ໃນຄິວ, ໂດຍບໍ່ສົນໃຈສິ່ງທີ່ຢູ່ໃນຄິວແລ້ວ.

ຫຼັງຈາກ event handler ສຳເລັດ, React ຈະ trigger ການ render ໃໝ່. ລະຫວ່າງການ render ໃໝ່, React ຈະປະມວນຜົນຄິວ. ຟັງຊັ່ນ updater ເຮັດວຽກລະຫວ່າງການ render, ສະນັ້ນ ຟັງຊັ່ນ updater ຕ້ອງ pure ແລະ ມີພຽງການ return ຜົນລັບເທົ່ານັ້ນ. ຢ່າພະຍາຍາມຕັ້ງຄ່າ state ຈາກພາຍໃນ ຫຼື ເອີ້ນໃຊ້ຜົນຂ້າງຄຽງອື່ນໆ. ໃນ Strict Mode, React ຈະເອີ້ນໃຊ້ຟັງຊັ່ນ updater ສອງເທື່ອ (ແຕ່ປະຜົນລັບທີ່ສອງ) ເພື່ອຊ່ວຍໃຫ້ທ່ານພົບຂໍ້ຜິດພາດ.

ກົດຂອງການຕັ້ງຊື່

ເປັນເລື່ອງປົກະຕິທີ່ຈະຕັ້ງຊື່ argument ຟັງຊັ່ນ updater ດ້ວຍຕົວອັກສອນໂຕທຳອິດຂອງຕົວແປ state ທີ່ກ່ຽວຂ້ອງ:

setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);

ຖ້າທ່ານຕ້ອງການ code ທີ່ລະອຽດເພີ່ມເຕີມ, ຫຼັກການທົ່ວໄປອີກແບບແມ່ນການເຮັດຊໍ້າຕົວແປ state ເຕັມເຊັ່ນ setEnabled(enabled => !enabled) ຫຼື ໃຊ້ຄຳນຳໜ້າເຊັ່ນ setEnabled(prevEnabled => !prevEnabled).

Recap

  • State ການຕັ້ງຄ່າບໍ່ປ່ຽນຕົວແປໃນການ render ທີ່ມີຢູ່ ແຕ່ຮ້ອງຂໍການ render ໃໝ່.
  • ອັບເດດ state ຂະບວນການຕອບສະໜອງຫຼັງຈາກ event handler ເຮັດວຽກແລ້ວ. ສິ່ງນີ້ເອີ້ນວ່າການ batch.
  • ຫາກຕ້ອງການອັບເດດ state ຫຼາຍຄັ້ງໃນ event ດຽວ, ທ່ານສາມາດໃຊ້ຟັງຊັ່ນ updater setNumber(n => n + 1).

Challenge 1 of 2:
ແກ້ໄຂຕົວນັບຄຳຂໍ

ທ່ານກຳລັງເຮັດວຽກກັບແອັບຕະຫຼາດຊື້ຂາຍງານສິລະປະທີ່ໃຫ້ຜູ້ໃຊ້ສົ່ງຄຳຊື້ຫຼາຍລາຍການສຳລັບລາຍການສິລະປະພ້ອມກັນໄດ້. ທຸກຄັ້ງທີ່ຜູ້ໃຊ້ກົດປຸ່ມ “Buy”, ຕົວນັບ “Pending” ຄວນເພີ່ມຂຶ້ນເທື່ອລະໜຶ່ງ. ຫຼັງຈາກສາມວິນາທີ, ຕົວນັບ “Pending” ຄວນຫຼຸດລົງ ແລະ ຕົວນັບ “Completed” ຄວນເພີ່ມຂຶ້ນ.

ເຖິງຢ່າງໃດກໍຕາມ, ຕົວນັບ “Pending” ບໍ່ເຮັດວຽກຕາມທີ່ຕັ້ງໃຈໄວ້. ເມື່ອທ່ານກົດ “Buy”, ມັນຈະຫຼຸດລົງເປັນ -1 (ເຊິ່ງບໍ່ຄວນເປັນໄປໄດ້!). ແລະ ຖ້າທ່ານຄິກໄວສອງຄັ້ງ, ຕົວນັບທັງສອງເບິ່ງຄືຈະເຮັດວຽກຢ່າງຄາດເດົາບໍ່ໄດ້.

ເກີດສິ່ງນີ້ໄດ້ແນວໃດ? ແກ້ໄຂໂຕນັບທັງສອງ.

import { useState } from 'react';

export default function RequestTracker() {
  const [pending, setPending] = useState(0);
  const [completed, setCompleted] = useState(0);

  async function handleClick() {
    setPending(pending + 1);
    await delay(3000);
    setPending(pending - 1);
    setCompleted(completed + 1);
  }

  return (
    <>
      <h3>
        Pending: {pending}
      </h3>
      <h3>
        Completed: {completed}
      </h3>
      <button onClick={handleClick}>
        Buy     
      </button>
    </>
  );
}

function delay(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}