๋ณธ๋ฌธ์œผ๋กœ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Action

: ๋น„๋™๊ธฐ ์ „ํ™˜์„ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜

 

(Before) Example1. passive process

function OrderCoffee({}) {
  const [coffee, setCoffee] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await orderCoffee(coffee);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    } 
    redirect("/order-success");
  };

  return (
    <div>
      <input value={coffee} onChange={(event) => setCoffee(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Order Coffee
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

 

๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , ์ด์— ๋Œ€์‘ํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ, ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๋Š” ์–‘์‹์„ ์ œ์ถœํ•  ๋•Œ API ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•œ ํ›„ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ ๊ณผ๊ฑฐ์—๋Š” ๋ณด๋ฅ˜ ์ƒํƒœ, ์˜ค๋ฅ˜, ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ, ์ˆœ์ฐจ์  ์š”์ฒญ ๋“ฑ์„ ์ˆ˜๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

(react-19) Example2. async transition

// Using pending state from Actions
function OrderCoffee({}) {
  const [coffee, setCoffee] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await orderCoffee(coffee);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/order-success");
    })
  };

  return (
    <div>
      <input value={coffee} onChange={(event) => setCoffee(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Order Coffee
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

 

 async transition์€ ์ฆ‰์‹œ isPending ์ƒํƒœ๋ฅผ true๋กœ ์„ค์ •ํ•˜๊ณ , ๋น„๋™๊ธฐ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•œ ํ›„, ๋ชจ๋“  ์ „ํ™˜์ด ๋๋‚˜๋ฉด isPending์„ false๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๋™์•ˆ ํ˜„์žฌ UI๋ฅผ responsive and interactiveํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

 

`useTransition`์€ React์˜ ๋™์‹œ์„ฑ ๋ชจ๋“œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ, ๋‚ด๋ถ€์ ์œผ๋กœ React์˜ ์Šค์ผ€์ค„๋Ÿฌ์™€ Fiber ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—…์˜ ์šฐ์„  ์ˆœ์œ„๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฐจ๋‹จ๋˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฐ˜์‘์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ณต์žกํ•œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

(react-19) Example3. useActionState

// Using <form> Actions and useActionState
function OrderCoffee({ coffee, setCoffee }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await orderCoffee(formData.get("coffee"));
      if (error) {
        return error;
      }
      redirect("/order-success");
      return null;
    },
    null,
  );

  return (
    <form action={submitAction}>
      <input type="text" name="coffee" />
      <button type="submit" disabled={isPending}>Order Coffee</button>
      {error && <p>{error}</p>}
    </form>
  );
}

 

 

์•ก์…˜(Actions)์„ ๊ธฐ๋ฐ˜์œผ๋กœ, ๋ฆฌ์•กํŠธ 19๋Š” ์ผ๋ฐ˜์ ์ธ ์•ก์…˜ ์ผ€์ด์Šค๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ํ›… React.useActionState๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์•ก์…˜์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ์ถœํ•˜๋Š” ์ž‘์—…์„ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ด ์ค๋‹ˆ๋‹ค.

 

useActionState๋Š” ํ•จ์ˆ˜(“์•ก์…˜”)๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„, ํ˜ธ์ถœํ•  ๋ž˜ํ•‘๋œ ์•ก์…˜์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์•ก์…˜๋“ค์ด ์กฐํ•ฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋ž˜ํ•‘๋œ ์•ก์…˜์ด ํ˜ธ์ถœ๋˜๋ฉด, useActionState๋Š” ์•ก์…˜์˜ ๋งˆ์ง€๋ง‰ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ดํ„ฐ๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์•ก์…˜์˜ ๋ณด๋ฅ˜ ์ƒํƒœ๋ฅผ ๋ณด๋ฅ˜๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

(react-19) Example4. ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      if (!formData) {
        setCoffeeImage(null);
        return null;
      }
      const coffeeName = formData.get("coffee");
      const shot = formData.get("shot");
      const temperature = formData.get("temperature");
      const coffeeSrc = await orderCoffee(coffeeName, temperature, shot);

      if (!coffeeSrc) {
        return "Failed to fetch coffee image";
      }

      setCoffeeImage(coffeeSrc);
      return null;
    },
    null,
  );

 

 

์ฐธ๊ณ 

React.useActionState๋Š” ์ด์ „์—๋Š” canary release ์—์„œ ReactDOM.useFormState๋ผ๊ณ  ๋ถˆ๋ ธ์œผ๋‚˜, ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋˜์—ˆ๊ณ  useFormState๋Š” ํ๊ธฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•