ເຮັດໃຫ້ Components Pure

ບາງຟັງຊັ່ນ JavaScript ແມ່ນ pure. ຟັງຊັ່ນ Pure ເຮັດການຄຳນວນເທົ່ານັ້ນ ແລະ ບໍ່ມີຫຍັງເພີ່ມເຕີມ. ການຂຽນ component ຂອງທ່ານໃຫ້ເປັນຟັງຊັ່ນ pure ເທົ່ານັ້ນຢ່າງເຄັ່ງຄັດ, ທ່ານສາມາດຫຼີກລ່ຽງຂໍ້ຜິດພາດທັງ class ແລະ ສິ່ງທີ່ຄາດບໍ່ເຖິງເມື່ອ codebase ຂອງທ່ານໃຫຍ່ຂຶ້ນ. ເພື່ອໃຫ້ໄດ້ຮັບຜົນປະໂຫຍດເຫຼົ່ານີ້, ມີກົດຢູ່ສອງສາມຂໍ້ທີ່ທ່ານຕ້ອງປະຕິບັດ.

You will learn

  • Pure ແມ່ນຫຍັງ ແລະ ຊ່ວຍໃຫ້ທ່ານຫຼີກລ່ຽງຂໍ້ຜິດພາດໄດ້ແນວໃດ
  • ວິທີເຮັດໃຫ້ component pure ໂດຍການປ້ອງກັນການປ່ຽນແປງຈາກຂັ້ນຕອນການສະແດງຜົນ
  • ວິທີໃຊ້ Strict MOde ເພື່ອຫາຂໍ້ຜິດພາດໃນ component ຂອງທ່ານ

ຄວາມ Pure: Components ເປັນ formulas

ໃນວິທະຍາສາດຄອມພີວເຕີ (ແລະ ໂດຍສະເພາະຄຳສັບຂອງ functinal programming), pure function ເປັນຟັງຊັ່ນທີ່ມີລັກສະນະດັ່ງນີ້:

  • ມັນຄິດເຖິງເລື່ອງຂອງໂຕເອງ. ມັນຈະບໍ່ປ່ຽນແປງ object ຫຼື ຕົວແປໃດໆທີ່ມີຢູ່ກ່ອນຈະຖືກເອີ້ນ.
  • Input ດຽວກັນ, output ດຽວກັນ. ເມື່ອພິຈາລະນາ input ດຽວກັນຟັງຊັ່ນ pure ຄວນ return ຜົນລັບດຽວກັນສະເໝີ.

ທ່ານອາດຄຸ້ນເຄີຍກັບຕົວຢ່າງໜຶ່ງຂອງຟັງຊັ່ນ pure: ສູດທາງຄະນິດສາດ.

ພິຈາລະນາສູດຄະນິດສາດນີ້: y = 2x.

ຖ້າ x = 2 ແລ້ວ y = 4. ສະເໝີ.

ຖ້າ x = 3 ແລ້ວ y = 6. ສະເໝີ.

ຖ້າ x = 3, y ບາງເທື່ອຈະບໍ່ເປັນ 9 ຫຼື –1 ຫຼື 2.5 ຂຶ້ນກັບຊ່ວງເວລາຂອງວັນ ຫຼື ສະຖານະການຂອງຕະຫຼາດຫຸ້ນ.

ຖ້າ y = 2x ແລະ x = 3, y ຈະເປັນ 6 ຢູ່ສະເໝີ.

ຖ້າເຮົາເອົາເຂົ້າເປັນຟັງຊັ່ນ JavScript, ໜ້າຕາຈະປະມານນີ້:

function double(number) {
return 2 * number;
}

ໃນຕົວຢ່າງດ້ານເທິງ, double ເປັນ ຟັງຊັ່ນ pure. ຖ້າທ່ານສົ່ງ 3, ມັນຈະ return 6. ສະເໝີ.

React ໄດ້ຮັບການອອກແບບຕາມແນວຄິດນີ້. React ຈະຖືວ່າທຸກ component ທີ່ທ່ານຂຽນເປັນຟັງຊັ່ນ pure. ເຊິ່ງໝາຍຄວາມວ່າ component React ທີ່ທ່ານຂຽນຈະຕ້ອງ return ຄ່າ JSX ດຽວກັນສະເໝີເມື່ອໄດ້ຮັບ input ດຽວກັນ:

function Recipe({ drinkers }) {
  return (
    <ol>    
      <li>Boil {drinkers} cups of water.</li>
      <li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
      <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
    </ol>
  );
}

export default function App() {
  return (
    <section>
      <h1>Spiced Chai Recipe</h1>
      <h2>For two</h2>
      <Recipe drinkers={2} />
      <h2>For a gathering</h2>
      <Recipe drinkers={4} />
    </section>
  );
}

ເມື່ອທ່ານສົ່ງ drinker={2} ຫາ Recipe. ມັນຈະ return JSX ທີ່ປະກອບມີ 2 cups of water. ສະເໝີ.

ຖ້າທ່ານສົ່ງ drinkers={4}, ມັນຈະ return JSX ທີ່ປະກອບມີ 4 cups of water. ສະເໝີ.

ຄືກັບສູດຄະນິດສາດ.

ທ່ານສາມາດຄິດວ່າ component ຂອງທ່ານເປັນສູດອາຫານ: ຖ້າທ່ານເຮັດຕາມ ແລະ ບໍ່ເພີ່ມສ່ວນປະສົມໃໝ່ລະຫວ່າງຂັ້ນຕອນການເຮັດອາຫານ, ທ່ານຈະໄດ້ອາຫານຈານເກົ່າທຸກເທື່ອ. “ຈານ” ເປັນ JSX ທີ່ component ເຮັດໜ້າທີ່ຕອບສະໜອງຕໍ່ React ເພື່ອ ສະແດງຜົນ.

A tea recipe for x people: take x cups of water, add x spoons of tea and 0.5x spoons of spices, and 0.5x cups of milk

Illustrated by Rachel Lee Nabors

ຜົນຂ້າງຄຽງ: ຜົນທີ່ຕາມມາ(ໂດຍບໍ່ໄດ້ຕັ້ງໃຈ)

ຂະບວນການສະແດງຜົນຂອງ React ຕ້ອງ pure ຢຸ່ສະເໝີ. Component ຄວນ return JSX ຂອງມັນເທົ່ານັ້ນ, ແລະ ບໍ່ ປ່ຽນແປງ object ຫຼື ຕົວແປໃດໆທີ່ມີຢູ່ກ່ອນໜ້າການສະແດງຜົນ-ເຊິ່ງຈະເຮັດໃຫ້ມັນບໍ່ pure!

ນີ້ແມ່ນ component ທີ່ຝ່າຝືນກົດນີ້:

let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

Component ນີ້ກຳລັງອ່ານ ແລະ ຂຽນຕົວແປ guest ທີ່ປະກາດໄວ້ທາງນອກ. ໝາຍຄວາມວ່າ ການເອີ້ນ component ນີ້ຫຼາຍເທື່ອມັນຈະສ້າງ JSX ທີ່ແຕກຕ່າງກັນ! ແລະ ຫຼາຍໄປກວ່ານັ້ນ, ຖ້າ component ອື່ນ ເອີ້ນ guest, ມັນຈະສ້າງ JSX ທີ່ແຕກຕ່າງກັນເຊັ່ນດຽວກັນ, ທັງນີ້ຂຶ້ນກັບເວລາສະແດງຜົນ! ນັ້ນບໍ່ສາມາດຄາດເດົາໄດ້.

ກັບມາທີ່ສູດຂອງເຮົາ y = 2x, ເຖິງວ່າຖ້າ x = 2, ພວກເຮົາບໍ່ສາມາດເຊື່ອໄດ້ວ່າ y = 4. ການທົດສອບຂອງພວກເຮົາຈະບໍ່ສຳເລັດ, ຜູ້ໃຊ້ຂອງເຮົາອາດຈະງົງ, ຄືກັບຍົນຈະຕົກລົງມາຈາກທ້ອງຟ້າ-ທ່ານຈະເຫັນໄດ້ວ່າສິ່ງນີ້ຈະເຮັດໃຫ້ເກີດຂໍ້ຜິດພາດທີ່ສັບສົນໄດ້ແນວໃດ!

ທ່ານສາມາດແກ້ໄຂ component ນີ້ໄດ້ໂດຍ ການສົ່ງ guest ເປັນ prop ແທນ:

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

ປັດຈຸບັນ component ຂອງທ່ານ pure, ເນື່ອງຈາກສິ່ງທີ່ JSX ມັນ return ຂຶ້ນກັບ prop guest ເທົ່ານັ້ນ.

ໂດຍທົ່ວໄປ, ທ່ານບໍ່ຄວນຄາດຫວັງໃຫ້ component ຂອງທ່ານສະແດງຜົນຕາມລຳດັບສະເພາະໃດໆ. ມັນບໍ່ສຳຄັນວ່າທ່ານຈະເອີ້ນ y = 2x ກ່ອນ ຫຼື ຫຼັງ y = 5x: ສູດທັງສອງຈະແຍກອອກຈາກກັນ. ໃນວິທີດຽວກັນ, ແຕ່ລະ component ຄວນ “ຄິດເພື່ອໂຕເອງເທົ່ານັ້ນ”, ແລະ ບໍ່ພະຍາຍາມປະສານ ຫຼື ເພິ່ງພາຜູ້ອື່ນໃນລະຫວ່າງການ render. ການ render ກໍຄືກັບບົດເສັງໃນໂຮງຮຽນ: ແຕ່ລະ component ຄວນຄຳນວນ JSX ດ້ວຍໂຕເອງ!

Deep Dive

ການກວດສອບຫາການຄຳນວນທີ່ບໍ່ pure ດ້ວຍ Strict Mode

ເຖິງວ່າທ່ານອາດຈະຍັງບໍ່ໄດ້ໃຊ້ທັງໝົດ, ແຕ່ໃນ React ມີ input ສາມປະເພດທີ່ທ່ານສາມາດອ່ານໃນຂະນະ render props, state, ແລະ context. ທ່ານຄວນຖືວ່າ input ເຫຼົ່ານີ້ມີໄວ້ສະເພາະ read-only ສະເໝີ.

ເມື່ອທ່ານຕ້ອງການ ປ່ຽນ ບາງຢ່າງຕາມ user input, ທ່ານຄວນໃຊ້ set state ແທນການຂຽນໄປຍັງຕົວແປ. ທ່ານບໍ່ຄວນປ່ຽນຕົວແປ ຫຼື object ທີ່ມີຢູ່ກ່ອນໃນຂະນະ component ຂອງທ່ານກຳລັງ render.

React ມີ “Strict Mode” ເຊິ່ງຈະເອີ້ນຟັງຊັ່ນຂອງແຕ່ລະ Component ສອງຄັ້ງລະຫວ່າງການພັດທະນາ. ໂດຍການເອີ້ນຟັງຊັ່ນ component ສອງຄັ້ງ, Strict Mode ຈະຊ່ວຍຄົ້ນຫາ component ທີ່ຝ່າຝືນກົດເຫຼົ່ານີ້.

ສັງເກດວ່າຕົວຢ່າງເກົ່າທີ່ສະແດງ “Guest #2”, “Guest #4” ແລະ “Guest #6” ແທນ “Guest #1”, “Guest #2”, ແລະ “Guest #3”. ຟັງຊັ່ນເດີມແມ່ນບໍ່ pure, ດັ່ງນັ້ນການເອີ້ນໃຊ້ມັນສອງຄັ້ງຈຶ່ງພັງ. ແຕ່ເວີຊັ່ນ pure ຈະເຮັດວຽກເຖິງວ່າຟັງຊັ່ນຈະຖືກເອີ້ນໃຊ້ສອງຄັ້ງທຸກຄັ້ງ. ຟັງຊັ່ນ pure ຈະຄຳນວນເທົ່ານັ້ນ, ສະນັ້ນການເອີ້ນໃຊ້ມັນສອງຄັ້ງຈະບໍ່ປ່ຽນແປງຫຍັງ—ຄືກັນກັບການເອີ້ນ double(2) ສອງຄັ້ງບໍ່ປ່ຽນແປງສິ່ງທີ່ return, ແລະ ການແກ້ໄຂ y = 2x ສອງຄັ້ງບໍ່ໄດ້ປ່ຽນ y ແມ່ນຫຍັງ. Input ດຽວກັນ, output ດຽວກັນສະເໝີ.

Strict Mode ບໍ່ມີຜົນຫຍັງໃນ production, ດັ່ງນັ້ນຈຶ່ງບໍ່ເຮັດໃຫ້ແອັບຊ້າລົງສຳລັບຜູ້ໃຊ້ຂອງທ່ານ. ຫາກຕ້ອງເລືອກໃຊ້ Strict Mode, ທ່ານສາມາດລວມ component root ຂອງທ່ານເປັນ <React.StrictMode>. ບາງ framework ແມ່ນເຮັດໂດຍເປັນຄ່າເລີ່ມຕົ້ນ.

Local mutation: ຄວາມລັບເລັກໆນ້ອຍຂອງ Component ຂອງທ່ານ

ໃນຕົວຢ່າງດ້ານເທິງ, ບັນຫາຄື component ປ່ຽນຕົວແປ ທີ່ມີຢູ່ກ່ອນແລ້ວ ໃນຂະນະ render. ເຊິ່ງມັກເອີ້ນວ່າ “mutation” ເພື່ອໃຫ້ຟັງແລ້ວນ່າຢ້ານຂຶ້ນ. ຟັງຊັ່ນ pure ຈະບໍ່ mutate ຕົວແປນອກຂອບເຂດຂອງຟັງຊັ່ນ ຫຼື object ທີ່ຖືກສ້າງກ່ອນຖືກເອີ້ນໃຊ້-ເຮັດໃຫ້ຕົວແປເຫຼົ່ານັ້ນບໍ່ pure!

ເຖີງຢ່າງໃດກໍຕາມ, ມັນເປັນເລື່ອງປົກະຕິທີ່ຈະປ່ຽນຕົວແປ ແລະ object ທີ່ທ່ານ ຫາກໍ ສ້າງຂຶ້ນໃນລະຫວ່າງການ render. ໃນຕົວຢ່າງນີ້, ທ່ານສ້າງ array [], ກຳນົດໃຫ້ຕົວແປ cups ແລ້ວຕາມດ້ວຍ push dozen cups ເຂົ້າໄປ:

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaGathering() {
  let cups = [];
  for (let i = 1; i <= 12; i++) {
    cups.push(<Cup key={i} guest={i} />);
  }
  return cups;
}

ຖ້າຕົວແປ cups ຫຼື array [] ຖືກສ້າງຢູ່ນອກຟັງຊັ່ນ TeaGathering, ນີ້ອາດເປັນບັນຫາໃຫຍ່! ທ່ານຈະປ່ຽນ object ທີ່ມີຢູ່ແລ້ວ ໂດຍການ push item ເຂົ້າໄປໃນ array.

ເຖິງຢ່າງໃດກໍຕາມ, ບໍ່ເປັນຫຍັງຖ້າທ່ານສ້າງ ລະຫວ່າງການ render ດຽວກັນ, ພາຍໃນ TeaGathering. ບໍ່ມີ code ຢູ່ດ້ານນອກຂອງ TeaGathering ຈະຮູ້ວ່າສິ່ງນີ້ເກີດຂຶ້ນ. ນີ້ເອີ້ນວ່າ “local mutation”-ມັນເໝືອນກັບຄວາມລັບເລັກໆນ້ອຍຂອງ component ຂອງທ່ານ.

ບ່ອນທີ່ທ່ານ ສາມາດ ເຮັດໃຫ້ເກີດຜົນຂ້າງຄຽງ

ໃນຂະນະທີ່ functional programming ຕ້ອງອາໄສຄວາມ pure ເປັນຢ່າງຫຼາຍ, ໃນບາງຈຸດ,​ບາງບ່ອນ ບາງສິ່ງ ກໍຕ້ອງປ່ຽນແປງ. ນີ້ເປັນຈຸດປະສົງຂອງການຂຽນໂປຣແກຣມ! ການປ່ຽນແປງເຫຼົ່ານີ້-ການອັບເດດໜ້າຈໍ, ການເລີ່ມ animation, ການປ່ຽນແປງຂໍ້ມູນ-ເອີ້ນວ່າ ຜົນຂ້າງຄຽງ. ສິ່ງເຫຼົ່ານີ້ຄືສິ່ງທີ່ເກີດຂຶ້ນ “ດ້ານຂ້າງ” ບໍ່ແມ່ນລະຫວ່າງການສະແດງຜົນ.

ໃນ React, ຜົນຂ້າງຄຽງມັກຈະຢູ່ໃນ event handlers. Event handler ແມ່ນຟັງຊັ່ນທີ່ React ເຮັດວຽກເມື່ອທ່ານດຳເນີນການບາງຢ່າງ-ຕົວຢ່າງ, ເມື່ອທ່ານຄິກປຸ່ມ. ເຖິງວ່າ event handler ແມ່ນຖືກປະກາດ ພາຍໃນ component ຂອງທ່ານ, ມັນບໍ່ເຮັດວຽກ ລະຫວ່າງ ການສະແດງຜົນ! ສະນັ້ນ event handler ບໍ່ຈຳເປັນຕ້ອງ pure.

ຫາກທ່ານໃຊ້ຕົວເລືອກອື່ນໝົດແລ້ວ ແລະ ບໍ່ພົບ event handler ທີ່ເໝາະສົມສຳລັບຜົນຂ້າງຄຽງຂອງທ່ານ, ທ່ານຍັງສາມາດແນບມັນກັບ JSX ທີ່ return ດ້ວຍການເອີ້ນ useEffect ໃນ component ຂອງທ່ານ. ສິ່ງນີ້ຈະບອກໃຫ້ React ດຳເນີນການພາຍຫຼັງ, ຫຼັງຈາກການ render, ເມື່ອອະນຸຍາດໃຫ້ມີຜົນຂ້າງຄຽງ. ເຖິງຢ່າງໃດກໍຕາມ, ວິທີນີ້ຄວນເປັນທາງເລືອກສຸດທ້າຍຂອງທ່ານ

ເມື່ອເປັນໄປໄດ້, ພະຍາຍາມສະແດງ logic ຂອງທ່ານດ້ວຍການ render ພຽງຢ່າງດຽວ. ທ່ານຈະປະຫຼາດໃຈວ່າສິ່ງນີ້ສາມາດພາທ່ານໄປໄກໄດ້ສໍ່າໃດ!

Deep Dive

ເປັນຫຍັງ React ຈຶ່ງສົນໃຈເລື່ອງ pure?

ການຂຽນຟັງຊັ່ນ pure ຕ້ອງໃຊ້ນິໄສ ແລະ ລະບຽບວິໄນ. ແຕ່ມັນຍັງປົດລ໋ອກໂອກາດທີ່ນ່າອັດສະຈັນອີກດ້ວຍ:

  • Component ຂອງທ່ານສາມາດເຮັດວຽກໃນສະພາບແວດລ້ອມທີ່ແຕກຕ່າງກັນ-ຕົວຢ່າງ, ເທິງເຊີເວີ! ເນື່ອງຈາກ return ຜົນລັບດຽວກັນສຳລັບ input ດຽວກັນ, ໜຶ່ງ component ສາມາດຕອບສະໜອງຄຳຂໍຂອງຜູ້ໃຊ້ຈຳນວນຫຼາຍໄດ້.
  • ທ່ານສາມາດປັບປຸງປະສິດທິພາບໄດ້ໂດຍ ການຂ້າມການ render component ທີ່ບໍ່ມີການປ່ຽນແປງ input. ສິ່ງນີ້ປອດໄພເພາະຟັງຊັ່ນ pure ຈະ return ຜົນລັບດຽວກັນສະເໝີ, ດັ່ງນັ້ນຈຶ່ງປອດໄພສຳລັບການ cache.
  • ຫາກຂໍ້ມູນບາງສ່ວນປ່ຽນແປງລະຫວ່າງການ render deep component tree, React ສາມາດເລີ່ມການ render ໃໝ່ໂດຍບໍ່ເສຍເວລາໃນການ render ທີ່ຫຼ້າສະໄໝໃຫ້ສຳເລັດ. Pure ເຮັດໃຫ້ປອດໄພທີ່ຈະຢຸດຄຳນວນໄດ້ທຸກເວລາ.

ທຸກໆ Feature React ໃໝ່ທີ່ເຮົາກຳລັງໃຊ້ປະໂຫຍດຈາກຄວາມ pure. ຕັ້ງແຕ່ການດຶງຂໍ້ມູນໄປຈົນເຖິງພາບເຄື່ອນໄຫວຈົນເຖິງປະສິດທິພາບ, ການຮັກສາ pure component ຈະຊ່ວຍປົດລັອກພະລັງຂອງ React paradigm.

Recap

  • Component ຕ້ອງ pure, ໝາຍຄວາມວ່າ:
    • ມັນຄິດເຖິງເລື່ອງຂອງໂຕເອງ. ບໍ່ຄວນປ່ຽນແປງ object ຫຼື ຕົວແປໃດໆທີ່ມີຢູ່ກ່ອນການ render.
    • input ດຽວກັນ, output ດຽວກັນ. ເມື່ອພິຈາລະນາ input ດຽວກັນ, component ຄວນ return JSX ດຽວກັນສະເໝີ.
  • ການ render ສາມາດເກີດຂຶ້ນໄດ້ຕະຫຼອດເວລາ, ສະນັ້ນ component ບໍ່ຄວນຂຶ້ນກັບລຳດັບການສະແດງຜົນຂອງກັນ ແລະ ກັນ.
  • ທ່ານບໍ່ຄວນ mutate input ໃດໆທີ່ component ຂອງທ່ານໃຊ້ສຳລັບການ render. ຊື່ງລວມ prop, state ແລະ context. ເພື່ອອັບເດດໜ້າຈໍ, “set” state ແທນການ mutate object ທີ່ມີຢູ່ກ່ອນ.
  • ພະຍາຍາມສະແດງ logic ຂອງ component ທ່ານໃນ JSX ທີ່ທ່ານ return. ເມື່ອທ່ານຕ້ອງການ “ປ່ຽນແປງສິ່ງ”, ທ່ານມັກຈະຕ້ອງເຮັດ event handler. ເປັນທາງເລືອກສຸດທ້າຍ, ທ່ານສາມາດໃຊ້ useEffect
  • ການຂຽນຟັງຊັ່ນ pure ຕ້ອງອາໄສການເຝິກຊ້ອມໜ້ອຍໜຶ່ງ, ແຕ່ຈະປົດລ໋ອກພະລັງຂອງ React paradigm.

Challenge 1 of 3:
ແປງໂມງເພ

Component ນີ້ພະຍາຍາມຕັ້ງຄ່າ class CSS ຂອງ <h1> ເປັນ "night" ໃນຊ່ວງເວລາຕັ້ງແຕ່ທ່ຽງຄືນຫາຫົກຊົ່ວໂມງໃນຕອນເຊົ້າ, ແລະ "day" ໃນຊ່ວງເວລາອື່ນໆ. ເຖິງຢ່າງໃດກໍຕາມ, ມັນບໍ່ເຮັດວຽກ. ທ່ານສາມາດແກ້ໄຂ component ນີ້ໄດ້ ຫຼື ບໍ່?

ທ່ານສາມາດກວດສອບວ່າວິທີແກ້ໄຂຂອງທ່ານເຮັດວຽກໄດ້ ຫຼື ບໍ່ ໂດຍການປ່ຽນ timezone ຂອງຄອມພີວເຕີຊົ່ວຄາວ. ເມື່ອເວລາປັດຈຸບັນຢູ່ລະຫວ່າງທ່ຽງຄືນຮອດຫົກໂມງເຊົ້າ, ໂມງຄວນປ່ຽນສີ!

export default function Clock({ time }) {
  let hours = time.getHours();
  if (hours >= 0 && hours <= 6) {
    document.getElementById('time').className = 'night';
  } else {
    document.getElementById('time').className = 'day';
  }
  return (
    <h1 id="time">
      {time.toLocaleTimeString()}
    </h1>
  );
}