State variable set செய்வது மற்றொரு render-ஐ queue செய்யும். ஆனால் சில நேரங்களில், அடுத்த render-ஐ queue செய்வதற்கு முன் value மீது பல operations செய்ய விரும்பலாம். இதைச் செய்ய, React state updates-ஐ எப்படி batch செய்கிறது என்பதைப் புரிந்துகொள்வது உதவும்.

நீங்கள் கற்றுக்கொள்ள போவது

  • ”Batching” என்றால் என்ன, பல state updates process செய்ய React அதை எப்படி பயன்படுத்துகிறது
  • ஒரே state variable-க்கு பல updates-ஐ தொடர்ச்சியாக apply செய்வது எப்படி

React state updates-ஐ batch செய்கிறது

setNumber(number + 1)-ஐ மூன்று முறை call செய்வதால், “+3” button click செய்தால் counter மூன்று முறை increment ஆகும் என்று நீங்கள் எதிர்பார்க்கலாம்:

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>
    </>
  )
}

ஆனால் முந்தைய section-இல் பார்த்தபடி, ஒவ்வொரு render-ன் state values fixed ஆக இருக்கும். ஆகவே முதல் render-ன் event handler-க்குள் number value எத்தனை முறை setNumber(1) call செய்தாலும் எப்போதும் 0 தான்:

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

ஆனால் இங்கே இன்னொரு factor செயல்படுகிறது. உங்கள் state updates process செய்வதற்கு முன் event handlers-இல் உள்ள அனைத்து code-மும் run ஆகும் வரை React காத்திருக்கும். அதனால் தான் இந்த setNumber() calls அனைத்திற்குப் பிறகே re-render நடக்கிறது.

இது restaurant-இல் order எடுக்கும் waiter-ஐ நினைவூட்டலாம். உங்கள் முதல் dish சொன்னவுடனே waiter kitchen-க்கு ஓடமாட்டார்! அதற்கு பதிலாக, நீங்கள் order முடிக்க, அதை மாற்ற, அதே table-இல் உள்ள மற்றவர்களிடமிருந்தும் orders எடுக்க காத்திருப்பார்.

Restaurant-இல் elegant cursor, React உடன் பல முறை order செய்கிறது; waiter பாத்திரத்தில் React உள்ளது. அவள் setState() பல முறை call செய்த பிறகு, waiter அவள் இறுதி order ஆக கேட்ட கடைசியை எழுதிக்கொள்கிறார்.

Illustrated by Rachel Lee Nabors

இதனால் பல state variables-ஐ, பல components-இலிருந்தும் கூட, அதிகமான re-renders trigger செய்யாமல் update செய்யலாம். ஆனால் இதன் அர்த்தம், உங்கள் event handler மற்றும் அதில் உள்ள code அனைத்தும் முடியும் வரை UI update ஆகாது என்பதும். Batching என்று அழைக்கப்படும் இந்த behavior, உங்கள் React app-ஐ மிகவும் வேகமாக run செய்யும். சில variables மட்டும் update ஆன “half-finished” renders போன்ற confusing நிலைகளை கையாள வேண்டியதையும் இது தவிர்க்கிறது.

Clicks போன்ற பல intentional events முழுவதும் React batch செய்யாது; ஒவ்வொரு click தனியாக handle செய்யப்படும். பொதுவாக safe ஆனபோது மட்டுமே React batching செய்கிறது என்று நம்பலாம். உதாரணமாக, முதல் button click form-ஐ disable செய்தால், இரண்டாவது click அதை மீண்டும் submit செய்யாது என்பதை இது உறுதிசெய்கிறது.

அடுத்த render-க்கு முன் அதே state-ஐ பல முறை update செய்தல்

இது uncommon use case; ஆனால் அடுத்த render-க்கு முன் அதே state variable-ஐ பல முறை update செய்ய விரும்பினால், setNumber(number + 1) போன்ற next state value pass செய்வதற்கு பதிலாக, queue-இல் முந்தைய value அடிப்படையில் next state-ஐ கணக்கிடும் function pass செய்யலாம், உதாரணமாக setNumber(n => n + 1). இது state value-ஐ replace செய்வதற்கு பதிலாக “state value மீது ஏதாவது செய்” என்று React-க்கு சொல்லும் வழி.

இப்போது counter-ஐ increment செய்து பாருங்கள்:

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 function என்று அழைக்கப்படுகிறது. அதை state setter-க்கு pass செய்தால்:

  1. Event handler-இல் உள்ள மற்ற code அனைத்தும் run ஆன பிறகு process செய்ய React இந்த function-ஐ queue செய்கிறது.
  2. அடுத்த render போது, React queue-ஐ கடந்து சென்று final updated state-ஐ உங்களுக்கு தருகிறது.
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);

Event handler execute செய்யும் போது React இந்த lines of code-ஐ எப்படி process செய்கிறது:

  1. setNumber(n => n + 1): n => n + 1 ஒரு function. React அதை queue-க்கு add செய்கிறது.
  2. setNumber(n => n + 1): n => n + 1 ஒரு function. React அதை queue-க்கு add செய்கிறது.
  3. setNumber(n => n + 1): n => n + 1 ஒரு function. React அதை queue-க்கு add செய்கிறது.

அடுத்த render போது useState call செய்யும்போது, React queue-ஐ process செய்கிறது. முந்தைய number state 0; ஆகவே முதல் updater function-க்கு n argument ஆக React அதையே pass செய்கிறது. பின்னர் உங்கள் முந்தைய updater function-ன் return value-ஐ அடுத்த updater-க்கு n ஆக pass செய்கிறது; இவ்வாறு தொடர்கிறது:

queued updatenreturns
n => n + 100 + 1 = 1
n => n + 111 + 1 = 2
n => n + 122 + 1 = 3

React 3-ஐ final result ஆக store செய்து, அதை useState-இலிருந்து return செய்கிறது.

அதனால் தான் மேலுள்ள example-இல் “+3” click செய்தால் value சரியாக 3 அளவுக்கு increment ஆகிறது.

State replace செய்த பிறகு update செய்தால் என்ன நடக்கும்

இந்த event handler பற்றி என்ன? அடுத்த render-இல் number என்னாக இருக்கும் என்று நினைக்கிறீர்கள்?

<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);
      }}>Number-ஐ அதிகரிக்கவும்</button>
    </>
  )
}

இந்த event handler React-க்கு செய்யச் சொல்வது:

  1. setNumber(number + 5): number 0, ஆகவே setNumber(0 + 5). React அதன் queue-க்கு 5 ஆக replace செய்” என்பதைக் add செய்கிறது.
  2. setNumber(n => n + 1): n => n + 1 ஒரு updater function. React அந்த function-ஐ அதன் queue-க்கு add செய்கிறது.

அடுத்த render போது, React state queue-ஐ process செய்கிறது:

queued updatenreturns
5 ஆக replace செய்”0 (பயன்படுத்தப்படாது)5
n => n + 155 + 1 = 6

React 6-ஐ final result ஆக store செய்து, அதை useState-இலிருந்து return செய்கிறது.

Note

setState(5) உண்மையில் setState(n => 5) போலவே வேலை செய்கிறது; ஆனால் n பயன்படுத்தப்படாது என்பதை நீங்கள் கவனித்திருக்கலாம்!

State update செய்த பிறகு replace செய்தால் என்ன நடக்கும்

இன்னொரு example முயற்சிப்போம். அடுத்த render-இல் number என்னாக இருக்கும் என்று நினைக்கிறீர்கள்?

<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);
      }}>Number-ஐ அதிகரிக்கவும்</button>
    </>
  )
}

இந்த event handler execute செய்யும் போது React இந்த lines of code-ஐ process செய்வது:

  1. setNumber(number + 5): number 0, ஆகவே setNumber(0 + 5). React அதன் queue-க்கு 5 ஆக replace செய்” என்பதைக் add செய்கிறது.
  2. setNumber(n => n + 1): n => n + 1 ஒரு updater function. React அந்த function-ஐ அதன் queue-க்கு add செய்கிறது.
  3. setNumber(42): React அதன் queue-க்கு 42 ஆக replace செய்” என்பதைக் add செய்கிறது.

அடுத்த render போது, React state queue-ஐ process செய்கிறது:

queued updatenreturns
5 ஆக replace செய்”0 (பயன்படுத்தப்படாது)5
n => n + 155 + 1 = 6
42 ஆக replace செய்”6 (பயன்படுத்தப்படாது)42

பின்னர் React 42-ஐ final result ஆக store செய்து, அதை useState-இலிருந்து return செய்கிறது.

சுருக்கமாக, setNumber state setter-க்கு நீங்கள் pass செய்வதை இவ்வாறு சிந்திக்கலாம்:

  • Updater function (எ.கா. n => n + 1) queue-க்கு add செய்யப்படும்.
  • வேறு எந்த value-யும் (எ.கா. number 5) ஏற்கனவே queued ஆனதை ignore செய்து “5 ஆக replace செய்” என்பதைக் queue-க்கு add செய்கிறது.

Event handler முடிந்த பிறகு, React re-render trigger செய்யும். Re-render போது, React queue-ஐ process செய்யும். Updater functions rendering போது run ஆகின்றன; ஆகவே updater functions pure ஆக இருக்க வேண்டும் மற்றும் result-ஐ மட்டும் return செய்ய வேண்டும். அவற்றுக்குள் state set செய்யவோ மற்ற side effects run செய்யவோ முயற்சிக்க வேண்டாம். Strict Mode-இல், பிழைகளை கண்டுபிடிக்க உதவ React ஒவ்வொரு updater function-ஐயும் இரண்டு முறை run செய்யும் (ஆனால் இரண்டாவது result-ஐ discard செய்யும்).

Naming conventions

Updater function argument-ஐ தொடர்புடைய state variable-ன் முதல் எழுத்துகளால் name செய்வது பொதுவானது:

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

மேலும் verbose code விரும்பினால், setEnabled(enabled => !enabled) போல முழு state variable name-ஐ repeat செய்வது அல்லது setEnabled(prevEnabled => !prevEnabled) போல prefix பயன்படுத்துவது மற்றொரு common convention.

Recap

  • State set செய்வது existing render-இல் variable-ஐ மாற்றாது; அது புதிய render ஒன்றை request செய்கிறது.
  • Event handlers run ஆகி முடிந்த பிறகு React state updates-ஐ process செய்கிறது. இதுவே batching என்று அழைக்கப்படுகிறது.
  • ஒரே event-இல் ஒரு state-ஐ பல முறை update செய்ய, setNumber(n => n + 1) updater function பயன்படுத்தலாம்.

Challenge 1 of 2:
Request counter-ஐ சரிசெய்யுங்கள்

User ஒரே நேரத்தில் art item-க்கு பல orders submit செய்ய உதவும் art marketplace app-இல் நீங்கள் வேலை செய்கிறீர்கள். User “Buy” button அழுத்தும் ஒவ்வொரு முறையும், “Pending” counter ஒன்று அதிகரிக்க வேண்டும். மூன்று seconds பிறகு, “Pending” counter குறைய வேண்டும்; “Completed” counter அதிகரிக்க வேண்டும்.

ஆனால் “Pending” counter intended போல நடக்கவில்லை. “Buy” அழுத்தும்போது, அது -1 ஆக குறைகிறது (இது சாத்தியமாக இருக்கக்கூடாது!). நீங்கள் வேகமாக இரண்டு முறை click செய்தால், இரண்டு counters-உம் unpredictable ஆக நடக்கிறது போல தெரிகிறது.

இது ஏன் நடக்கிறது? இரண்டு counters-யையும் சரிசெய்யுங்கள்.

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}>
        வாங்கு
      </button>
    </>
  );
}

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