React 상태관리 라이브러리!

시은 ㄳㄳ

개쉽다! Zustand 사용법 - React 상태관리 라이브러리

// 예시코드
// userStore.jsx

import { create } from "zustand";

// store에 저장해서 관리할 데이터들
const useUserStore = create((set) => ({
  id: null,
  email: "",
  name: "",
  profileImageUrl: "",
  phoneNumber: "",
  birthday: "",
  birthyear: "",
  gender: "",
  detailAddr: "",
  defaultAddr: "",
  zipCode: "",

  // 상태를 업데이트하는 함수
  updateUserStore: (key, value) =>
    set((state) => ({
      ...state,
      [key]: value,
    })),

  // 상태 초기화 함수
  resetUserStore: () =>
    set({
      id: null,
      email: "",
      name: "",
      profileImageUrl: "",
      phoneNumber: "",
      birthday: "",
      birthyear: "",
      gender: "",
      detailAddr: "",
      defaultAddr: "",
      zipCode: "",
    }),
}));

export default useUserStore;

// 실제 사용 예시
// InputProfile.jsx

import SignupLogo from "/imgs/signupLogo.png";
import useUserStore from "../Store/UserStore"; // useUserStore를 import
import { useNavigate } from "react-router-dom";

function InputProfile() {
	// store에서 관리할 data들 설정
  const updateUserStore = useUserStore((state) => state.updateUserStore);
  const myName = useUserStore((state) => state.name);
  const myGender = useUserStore((state) => state.gender);
  const myBirthDay = useUserStore((state) => state.birthday);
  const myEmail = useUserStore((state) => state.email);
  const myPhoneNumber = useUserStore((state) => state.phoneNumber);
  const navigate = useNavigate();

  const handleName = (event) => {
    updateUserStore("name", event.target.value);
  };

  const handleEmail = (event) => {
    updateUserStore("email", event.target.value);
  };

  const handleBirthday = (event) => {
    updateUserStore("birthday", event.target.value);
  };

  const handlePhoneNumber = (event) => {
    updateUserStore("phoneNumber", event.target.value);
  };

  const handleGender = (genderValue) => {
    updateUserStore("gender", genderValue);
  };

  return (
    <div className="sub-layer gap-4">
      <img src={SignupLogo} alt="회원가입로고" className="m-3" />
      <div className="flex w-[330px] justify-between">
        <input
          type="text"
          placeholder="이름을 입력해주세요."
          className="common-btn signup-font h-[50px] w-[160px] border-[2.5px] bg-white p-3"
          value={myName}
          onChange={handleName}
        />
        <div className="flex gap-3">
          <button
            className={`common-btn ${myGender === "male" ? "bg-blue-400" : "bg-gray-400"}`}
            onClick={() => handleGender("male")}
          >
            남자
          </button>
          <button
            className={`common-btn ${myGender === "female" ? "bg-red-400" : "bg-gray-400"}`}
            onClick={() => handleGender("female")}
          >
            여자
          </button>
        </div>
      </div>

      <input
        type="text"
        placeholder="생년/월/일 8자리를 양식에 따라 입력해주세요"
        className="common-btn signup-font h-[50px] w-[330px] border-[2.5px] bg-white p-3"
        value={myBirthDay}
        onChange={handleBirthday}
      />

      <input
        type="text"
        placeholder="이메일을 입력해주세요."
        className="common-btn signup-font h-[50px] w-[330px] border-[2.5px] bg-white p-3"
        value={myEmail}
        onChange={handleEmail}
      />

      <input
        type="text"
        placeholder="연락처를 입력해주세요."
        className="common-btn signup-font h-[50px] w-[330px] border-[2.5px] bg-white p-3"
        value={myPhoneNumber}
        onChange={handlePhoneNumber}
      />

      <button
        className="common-btn h-full max-h-[50px] w-full max-w-[284px]"
        onClick={() => navigate("/signup")}
      >
        다음
      </button>
    </div>
  );
}

export default InputProfile;
// 2024-04-04 수정
import SignupLogo from "/imgs/signupLogo.png";
import useUserStore from "../Store/UserStore";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { useState } from "react";

function InputProfile() {
  const updateUserStore = useUserStore((state) => state.updateUserStore);
  const myName = useUserStore((state) => state.name);
  const myGender = useUserStore((state) => state.gender);
  const [myBirthDay, setMyBirthday] = useState("");
  const myEmail = useUserStore((state) => state.email);
  const myPhoneNumber = useUserStore((state) => state.phoneNumber);
  const navigate = useNavigate();

  // 데이터 유효성 검사를 위한 useState
  const [nameErr, setNameErr] = useState("");
  const [genderErr, setGenderErr] = useState("");
  const [birthErr, setBirthErr] = useState("");
  const [emailErr, setEmailErr] = useState("");
  const [phoneNumErr, setPhoneNumErr] = useState("");
  const [totalErr, setTotalErr] = useState("")

  // 이름 input에 입력하는 값을 store의 name에 저장
  const handleName = (event) => {
    const inputName = event.target.value;
    updateUserStore("name", inputName);
    if (inputName.length < 1 || inputName.length > 10) {
      setNameErr("이름은 1자 이상 10자 이하이어야 합니다.");
    } else {
      setNameErr("");
    }
  };

  // e-mail input에 입력하는 값을 store의 email에 저장
  const handleEmail = (event) => {
    const inputEmail = event.target.value;
    updateUserStore("email", inputEmail);

    // 이메일 형식 검사 정규식
    const patternEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/;
    if (!patternEmail.test(inputEmail) || inputEmail > 50) {
      setEmailErr("이메일을 올바르게 입력해주세요.");
    } else {
      setEmailErr("");
    }
  };

  // 생년월일 input에 입력하는 값을 store의 birthday에 저장
  const handleBirthday = (event) => {
    const inputBirthday = event.target.value;
    setMyBirthday(inputBirthday);
    if (!/^\\d{0,8}$/.test(inputBirthday) || inputBirthday.length !== 8) {
      setBirthErr("생년월일을 올바르게 입력해주세요.");
    } else {
      setBirthErr("");
    }
  };

  // 연락처 input 에 입력하는 값을 store의 phoneNumber에 저장
  const handlePhoneNumber = (event) => {
    const inputPhoneNum = event.target.value;
    updateUserStore("phoneNumber", inputPhoneNum);

    // 전화번호 형식 검사 정규식
    const patternPhoneNum = /^\\d{10,11}$/;
    if (!patternPhoneNum.test(inputPhoneNum)) {
      setPhoneNumErr("연락처를 올바르게 입력해주세요.");
    } else {
      setPhoneNumErr("");
    }
  };

  const handleGender = (genderValue) => {
    updateUserStore("gender", genderValue);
    if (!genderValue) {
      setGenderErr("성별을 선택해주세요");
    } else {
      setGenderErr("");
    }
  };

  const handleNextSignupPage = () => {
    // if (nameErr === "" && genderErr === "" && birthErr === "" && emailErr === "" && phoneNumErr === "" &&
    // myName && myGender && myBirthDay && myEmail && myPhoneNumber) {
    const birthyear = myBirthDay.substring(0, 4);
    const birthday = myBirthDay.substring(4);
    axios
      .put(
        import.meta.env.VITE_BASE_URL + "/api/consumers",
        {
          "name": myName,
          "email": myEmail,
          "phoneNumber": myPhoneNumber,
          "birthyear": birthyear,
          "birthday": birthday,
          "gender": myGender,
        },
        {
          headers: {
            Authorization: `Bearer ${localStorage.getItem("access-token")}`,
          },
        },
      )
      .then((res) => {
        console.log(res);
        console.log("정보 수정 성공");
        navigate("/signup");
      })
      .catch((err) => {
        console.error(err);
        console.log("정보 수정 실패");
        setTotalErr("모든 정보를 올바르게 입력해주세요.")
      });
    // }
  };

  return (
    <div className="sub-layer top-[60px] gap-4">
      <img src={SignupLogo} alt="회원가입로고" className="m-3" />
      <div className="flex w-[330px] justify-between">
        <input
          type="text"
          placeholder="이름을 입력해주세요."
          className="common-btn signup-font h-[50px] w-[160px] border-[2.5px] bg-white p-3"
          value={myName}
          onChange={handleName}
          required
        />
        <div className="flex gap-3">
          <button
            className={`common-btn ${myGender === "male" ? "bg-blue-400" : "bg-gray-400"}`}
            onClick={() => handleGender("male")}
          >
            남자
          </button>
          <button
            className={`common-btn ${myGender === "female" ? "bg-red-400" : "bg-gray-400"}`}
            onClick={() => handleGender("female")}
          >
            여자
          </button>
        </div>
      </div>

      {nameErr && <p className="signup-font text-red-500">{nameErr}</p>}
      {genderErr && <p className="signup-font text-red-500">{genderErr}</p>}

      <input
        type="text"
        placeholder="생년/월/일 8자리를 입력해주세요"
        className="common-btn signup-font h-[50px] w-[330px] border-[2.5px] bg-white p-3"
        value={myBirthDay}
        onChange={handleBirthday}
        required
      />
      {birthErr && <p className="signup-font text-red-500">{birthErr}</p>}

      <input
        type="text"
        placeholder="이메일을 입력해주세요."
        className="common-btn signup-font h-[50px] w-[330px] border-[2.5px] bg-white p-3"
        value={myEmail}
        onChange={handleEmail}
        required
      />
      {emailErr && <p className="signup-font text-red-500">{emailErr}</p>}

      <input
        type="number"
        placeholder="연락처를 입력해주세요."
        className="common-btn signup-font h-[50px] w-[330px] border-[2.5px] bg-white p-3"
        value={myPhoneNumber}
        onChange={handlePhoneNumber}
        required
      />
      {phoneNumErr && <p className="signup-font text-red-500">{phoneNumErr}</p>}

      <button
        className="common-btn h-full max-h-[50px] w-full max-w-[284px]"
        onClick={handleNextSignupPage}
      >
        다음
      </button>
      {totalErr && <p className="signup-font text-red-500">{totalErr}</p>}
    </div>
  );
}

export default InputProfile;

zustand persist를 이용하여 storage에 전역 상태 저장하기

// wrtn 코드
import { create } from "zustand";
import { persist } from "zustand/middleware";

const useUserStore = create(
  persist(
    (set) => ({
      id: null,
      email: "",
      name: "",
      profileImageUrl: "",
      phoneNumber: "",
      birthday: "",
      birthyear: "",
      gender: "",
      detailAddr: "",
      defaultAddr: "",
      zipCode: "",

      // 상태를 업데이트하는 함수
      updateUserStore: (key, value) =>
        set((state) => ({
          ...state,
          [key]: value,
        })),

      // 상태 초기화 함수
      resetUserStore: () =>
        set({
          id: null,
          email: "",
          name: "",
          profileImageUrl: "",
          phoneNumber: "",
          birthday: "",
          birthyear: "",
          gender: "",
          detailAddr: "",
          defaultAddr: "",
          zipCode: "",
        }),
    }),
    {
      name: "userStore", // 로컬 스토리지에 저장될 때 사용될 키 이름
      getStorage: () => localStorage, // 사용할 스토리지 종류를 설정 (예: localStorage)
    }
  )
);

export default useUserStore;