React 비동기 데이터 처리 심화

cloud2000·2025년 11월 15일

useFormStatus

  • form의 제출 상태를 확인할 수 있음. data에는 폼을 제출할 때 입력한 값들을 담고 있는 FormData 타입의 객체임.
  
import { useFormStatus } from "react-dom";

function UpdateName() {
	const { pending, data } = useFormStatus();

	return (
		<>
			<input name="name" />
			<button disabled={pending}>변경</button>
			{data && <p>{data.get("name")} as string</p>}
		</>
	);
}
export default UpdateName;
  • form 데이터가 submit 중일때 submitting ... 표시하기
"use client";

import { useFormStatus } from "react-dom";

export default function MealFormSubmit() {
	const { pending } = useFormStatus();

	return (
		<button disabled={pending}>
			{pending ? "Submitting ..." : "Share Meal"}
		</button>
	);
}
  • ImagePicker 만들기
"use client";

import { useState, useRef } from "react";
import classes from "./image-picker.module.css";
import Image from 'next/image'

export default function ImagePicker({ label, name }) {
	const [pickedImage, setPickedImage] = useState();
	
	const imageInput = useRef();

	function handlePickClick() {
		imageInput.current.click();
	}

  function handleImageChange(event) {
    const file = event.target.files[0];

    if (!file) {
      return;
    }

    const fileReader = new FileReader();

    fileReader.onload = () => {
      setPickedImage(fileReader.result);
    };

    fileReader.readAsDataURL(file);
  }

	return (
		<div className={classes.picker}>
			<label htmlFor="image">{label}</label>
			<div className={classes.controls}>
        <div className={classes.preview}>
          {!pickedImage && <p>No image picked yet.</p>}
          {pickedImage && (
            <Image
              src={pickedImage}
              alt="The image selected by the user."
              fill
            />
          )}
        </div>				
				<input
					className={classes.input}
					type="file"
					id={name}
					accept="image.png, image/jpeg"
					name={name}
					ref={imageInput}
					onChange={handleImageChange}
					required
				/>
				<button
					className={classes.button}
					type="button"
					onClick={handlePickClick}
				>
					Pick an Image
				</button>
			</div>
		</div>
	);
}

UseActionState

"use client";

import ImagePicker from "@/components/meals/image-picker";
import classes from "./page.module.css";
import { shareMeal } from "@/lib/actions";
import MealFormSubmit from "@/components/meals/meals-form-submit";
import { useActionState, useState, useEffect } from "react";

export default function ShareMealPage() {
	// form submit시 필수필드 등의 check하여 오류 메시지 출력. 입력한 데이터를 유지
	const [state, formAction] = useActionState(shareMeal, { message: null });

	const [isLoading, setIsLoading] = useState(false);

	if (isLoading) {
		console.log("isLoading ...");
	}

	useEffect(() => {
		setIsLoading(true);

		console.log("share::useEffect() called");

		setIsLoading(false);
	}, [isLoading]);

	return (
		<>
			<header className={classes.header}>
				<h1>
					Share your <span className={classes.highlight}>favorite meal</span>
				</h1>
				<p>Or any other meal you feel needs sharing!</p>
			</header>
			<main className={classes.main}>
				<form className={classes.form} action={formAction}>
					<div className={classes.row}>
						<p>
							<label htmlFor="name">Your name</label>
							<input type="text" id="name" name="name" required />
						</p>
						<p>
							<label htmlFor="email">Your email</label>
							<input type="email" id="email" name="email" required />
						</p>
					</div>
					<p>
						<label htmlFor="title">Title</label>
						<input type="text" id="title" name="title" required />
					</p>
					<p>
						<label htmlFor="summary">Short Summary</label>
						<input type="text" id="summary" name="summary" required />
					</p>
					<p>
						<label htmlFor="instructions">Instructions</label>
						<textarea
							id="instructions"
							name="instructions"
							rows="10"
							required
						></textarea>
					</p>
					<ImagePicker label="Your image" name="image" />
					{state.message && <p>{state.message}</p>}
					<p className={classes.actions}>
						{/* useFormStatus() webhook을 이용하여 submit 중일때를 판단하여 버튼명을 변경함 */}
						<MealFormSubmit />
					</p>
				</form>
			</main>
		</>
	);
}
profile
클라우드쟁이

0개의 댓글