React

[React] Hooks - useState

wwxs 2024. 8. 31. 20:59

Hooks

  • 리액트 '함수형 컴포넌트'  에서 사용할 수 있는 기능

useState

  • React에서 제공하는 Hook 중 하나
  • 함수형 컴포넌트 내에서 상태를 관리 하는 기능
  • 호출 시 변화되는 상태값과 해당 상태값을 업데이트 하는 함수를 제공
  • 호출 시 상태 업데이트 함수는 비동기적 처리가 기본으로 진행
  • 상태 변경 시 컴포넌트의 재 렌더링을 유발

기본 구문

const [state, setState] = useState<type>(initialValue);

  • state: 현재의 상태값(변수)
  • setState: 상태를 업데이트 하는 함수
  • initialValue: 상태의 초기값
더보기
const [count, setCount] = useState<number>(0);
const handelUpclick = () => {
    // 1) 상태 설정 함수를 그대로 사용
    // >> 이전의 상태를 직접 참조
    //    ! 주로 현재(이전) 값과 관련없는 변화가 이루어질 경우 사용!
    // setCount(count + 1) // 0 + 1
    // setCount(count + 1) // 0 + 1

    // 2) 함수형 업데이트를 사용
    // >> 이전 상태 값을 기반으로 상태를 업데이트 하는 경우 (권장)
    setCount(prevCount => prevCount + 1); // 0 + 1
    setCount(prevCount => prevCount + 1); // 1 + 1
    setCount(prevCount => prevCount + 1); // 2 + 1
  }
  const handleDownClick = () => {
    setCount(prevCount => prevCount - 1);
  }
  const handleDownClick = () => {
    setCount(prevCount => prevCount - 1);
  }

 

(예시 코드)

더보기
export default function UseState03() {
  //# 클릭 이벤트 핸들러
  const [count, setCount] = useState<number>(0);
  const handleIncrementButton = (e: React.MouseEvent<HTMLButtonElement>) => {
    setCount(prevcount => prevcount + 1);
    console.log(e.target);
  };

  //# 입력 이벤트 핸들러
  const [input, setInput] = useState<string>('');
  const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInput(e.target.value);
    console.log(e.target);
  };

  //# 키보드 이벤트 핸들러
  // >> 'Enter' 키 클릭에 대한 반응을 처리
  const [enter, setEnter] = useState<string>('');
  const handleKeyboard = (e: React.KeyboardEvent<HTMLInputElement>) => {
    console.log(e.key);
    if(e.key === 'Enter'){
      console.log('Enter 키가 눌려졌습니다.');
      // 입력된 내용을 배열의 요소에 추가
      // , 다양한 로직 활용
      setEnter(e.currentTarget.value);
    }
  };

 

(회원가입 구현)

더보기

 

import React, { useState } from "react";

/*
! useState를 사용한 이벤트 처리 & 상태 관리

? 요구 사항 정리

1. 폼 필드
: 사용자 아이디, 비밀번호, 이메일 주소 입력 (문자열 데이터)

2. 입력 유효성 검사
: 모든 필드 입력창은 비워져 있을 수 X

3. 상태 관리
: 입력값은 객체로 하나의 useState를 통해 관리 (formData)

4. 폼 제출
: 폼 제출 시 모든 입력 값이 콘솔에 출력 (객체 자체를 출력)
>> 입력 조건을 만족하지 않는 경우(유효성 검사 불일치 시) - 오류 메시지 출력
>> 오류 메시지 관리도 컴포넌트 내에서 상태 관리
*/

/*
! 작업 순서
- 폼과 필요한 입력 필드 생성
- 입력 필드에 대한 상태 로직 관리 (onChange)
- 입력 유효성 검사 추가, 오류 메시지 표시 로직 작성
- 폼 제출 함수 구현, 콘솔에 입력 데이터 출력
*/

interface IformData {
  id: string;
  password: string;
  email: string;
}

export default function UseState04() {
  //! 폼 데이터 상태 관리
  const [formData, setFromData] = useState<IformData>({
    id: "",
    password: "",
    email: "",
  });

  //! 폼 입력 오류 메시지 상태 관리
  const [errors, setErrors] = useState<IformData>({
    id: "",
    password: "",
    email: "",
  });

  //! 각 입력 필드 변수 선언 (비구조화 할당)
  const { id, password, email } = formData;

  //! 폼 제출 이벤트를 처리하는 이벤트 핸들러
  const handleSignUpSubmit = (e: React.FormEvent) => {
    //? 폼 제출에 대한 기본 동작을 방지
    e.preventDefault();

    //? 임시 오류 메시지 객체 생성
    // >> 아이디, 비밀번호, 이메일 순으로 오류 메시지를 담아두는 객체
    // >> 최종 유효성 검사가 끝나면 해당 객체를 setErrors에 전달
    let tempErrors = {
      id: "",
      password: "",
      email: "",
    };

    //? 폼의 유효성 상태를 추적하는 변수
    // : boolean 타입의 변수
    // >> 하나라도 유효하지 않으면 false로 지정
    let isValid = true;

    // == 아이디 유효성 검사 ==
    if (!id) {
      // 아이디 입력 필드가 비워져 있으면
      tempErrors.id = "아이디를 입력해주세요."; // 오류 메시지 설정
      isValid = false;
    }
    // == 비밀번호 유효성 검사 ==
    if (!password) {
      // 비밀번호 입력 필드가 비워져 있으면
      tempErrors.password = "비밀번호를 입력해주세요."; // 오류 메시지 설정
      isValid = false;
    }
    // == 이메일 유효성 검사 ==
    if (!email) {
      // 이메일 입력 필드가 비워져 있으면
      tempErrors.email = "이메일를 입력해주세요."; // 오류 메시지 설정
      isValid = false;
    }

    //? 오류 상태를 업데이트
    setErrors(tempErrors);

    //? 모든 입력이 유효한 경우
    // >> 콘솔에 내용 출력
    // >> 입력 필드 초기화
    if (isValid) {
      console.log("회원 가입 데이터: ", formData);
      alert(`회원가입을 축하합니다 ${id}님`);

      // 데이터 활용 후 필드 초기화
      setFromData({
        id: "",
        password: "",
        email: "",
      });
    }
  };
  //! 입력 필드의 변경을 감지하는 이벤트 핸들러
  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    // 이벤트 객체에서 입력 필드의 이름과 값을 추출
    const { name, value } = e.target;

    setFromData({
      // 기존 폼 데이터의 값을 복사 (스프레드 연산자)
      ...formData,

      // 변경된 필드의 값을 업데이트
      [name]: value
    });
  };

  return (
    <div
      style={{
        margin: "20px",
        padding: "20px",
        border: "1px solid #ddd",
        textAlign: "center",
      }}
    >
      <h3>회원가입 구현</h3>
      <form onSubmit={handleSignUpSubmit}>
        <div>
          <label>
            아이디:
            <input
              type="text"
              name="id"
              value={id}
              onChange={handleInputChange}
            />
          </label>
          {/* {errors.id && <p style={{ color: "red" }}>{errors.id}</p>} */}
        </div>
        <div>
          <label>
            비밀번호:
            <input
              type="text"
              name="password"
              value={password}
              onChange={handleInputChange}
            />
          </label>
          {/* {errors.password && <p style={{ color: "red" }}>{errors.password}</p>} */}
        </div>
        <div>
          <label>
            이메일:
            <input
              type="text"
              name="email"
              value={email}
              onChange={handleInputChange}
            />
          </label>
          {/* {errors.email && <p style={{ color: "red" }}>{errors.email}</p>} */}
          {errors.id ? (
            <p style={{ color: "red" }}>{errors.id}</p>
          ) : errors.password ? (
            <p style={{ color: "red" }}>{errors.password}</p>
          ) : (
            <p style={{ color: "red" }}>{errors.email}</p>
          )}
        </div>
        <button type="submit">회원가입</button>
      </form>
    </div>
  );
}

 

useState를 사용한 다양한 타입 상태 관리

더보기

 

interface IUser {
  id: number;
  name: string;
}

export default function UseState05() {
  const [count, setCount] = useState<number>(0);
  const [name, setName] = useState<string>("");
  const [isVisiable, setIsVisiable] = useState<boolean>(false);
  const [user, setUser] = useState<IUser>({
    id: 0,
    name: "",
  });
  // 배열의 경우 초기값에 주로 [] 빈 배열 설정
  const [items, setItems] = useState<string[]>([]);

  const handleUserChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;

    setUser({
      ...user,

      [name]: value,
    });

  }
  const addItem = () => {
    const newItem = `Item ${items.length + 1}`;

    setItems([...items, newItem]);
  }
;
  return (
    <div
      style={{
        margin: "20px",
        border: "1px solid blue",
        padding: "20px",
      }}
    >
      <h5>여러 타입의 상태 관리</h5>

      {/* 숫자형: 카운터 증가 버튼 */}
      <p>count: {count}</p>
      <button
        onClick={() => {
          setCount((prevcount) => prevcount + 1);
        }}
      >
        증가
      </button>

      {/* 문자열: 사용자 이름 입력 필드 */}
      <p>Name: {name}</p>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />

      {/* 논리형: 토글 버튼 */}
      <p>Visiable? : {isVisiable ? "yes" : "no"}</p>
      <button onClick={() => setIsVisiable(!isVisiable)}>토글 버튼</button>

      {/* 객체: 사용자 정보 수정 입력 필드 */}
      {/* 
      객체의 속성 데이터는 HTML 영역 내에 출력 가능 
      >> 객체 구조 자체는 출력 불가
      >> 객체를 문자열 형식(JSON 형식으로 변환 후)으로 출력
      */}
      <p>
        User: {user.id} {user.name}
      </p>
      <p>User2: {JSON.stringify(user)}</p>

      <input
        type="text"
        name="name"
        value={user.name}
        placeholder="사용자 이름"
        onChange={handleUserChange}
      />
      <input
        type="number"
        name="id"
        value={user.id}
        placeholder="사용자 아이디"
        onChange={handleUserChange}
      />

      {/* 배열: 배열 요소 추가 */}
      <p>Items: {items}</p>
      <button onClick={addItem}>아이템 추가</button>
    </div>
  );
}

 

컴포넌트 트리 안에서의 상태

  • 상태를 컴포넌트 트리의 아래로 전달
  • 부모에서 자식 컴포넌트로 상태 전달

 

상태(State) VS 속성(Props)

 

1) 상태

  • 컴포넌트 내부에서 관리되는 데이터
  • 상태가 변겨오디면 렌더링을 유발(업데이트)
  • 컴포넌트가 자기 자신의 상태 변경 가능

2) 속성

  • 부모 컴포넌트(외부)로 부터 받은 데이터
  • 컴포넌트 간의 데이터 전달에 사용
  • 읽기전용 데이터: 자식 컴포넌트에서 수정 불가
더보기
type UserType = {
  username: string;
  height: number;
};
const initialValue: UserType = {
  username: "홍동현",
  height: 181,
};

export default function UseState06() {
  const [userInfo, setUserInfo] = useState<UserType>(initialValue);

  // 자식 컴포넌트에게 전달할 데이터 관리
  const [submittedData, setSubmittedData] = useState<UserType | undefined>(initialValue);

  const { username, height } = userInfo;

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setUserInfo({
      ...userInfo,
      [name]: value,
    });
  };

  const handleSubmit = () => {
    // 자식 컴포넌트에 데이터 전달
    setSubmittedData(userInfo)
  }
  return (
    <div>
      <input
        type="text"
        placeholder="이름"
        name="username"
        value={username}
        onChange={handleInputChange}
      />
      <input
        type="text"
        placeholder="키"
        name="height"
        value={height}
        onChange={handleInputChange}
      />
      <button onClick={handleSubmit}>확인</button>
      <ChildComponent userData={submittedData}/>
    </div>
  );
}

'React' 카테고리의 다른 글

[React] Hooks - useEffect  (0) 2024.08.31
[React] Hooks - useRef  (0) 2024.08.31
[React] Handler  (0) 2024.08.29
[React] Rendering  (0) 2024.08.29
[React] Props  (0) 2024.08.29