TIL_2024_01_31

이종현·2024년 1월 31일
0

Today_I_Learned

목록 보기
130/145
post-thumbnail

Today 요약

  1. 프로젝트
  2. 리액트 강의

1. What I Learned?

리액트 강의

버튼과 폼 연결하기

버튼의 form 속성에 form 태그의 id 값을 명시한다. 그럼 버튼 type을 submit으로 하는 것과 같다.

const PaymentButton = () => (
  <div className="PaymentButton">
    <Button form={"order-form"}>
      결제하기
    </Button>
  </div>
);

getFiledProps

자주 사용하는 prop들 모아놓고 함수로 활용하기

const getFieldProps = (name) => {
    const value = values[name];
    const onBlur = handleBlur;
    const onChange = handleChange;

    return {
      name,
      value,
      onBlur,
      onChange,
    };
  };

FormContext

useForm이라는 커스텀 훅을 만들어서 회원가입, 로그인같은 폼을 사용하는 곳에 코드를 좀 더 재사용 가능하고 예측하기 쉽게 구현하기

import React from "react";

export const useForm = ({ initialValue, validate, onSubmit }) => {
  const [values, setValues] = React.useState(initialValue);
  const [errors, setErrors] = React.useState({});
  const [touched, setTouched] = React.useState({});

  const handleChange = (e) => {
    setValues({
      ...values,
      [e.target.name]: e.target.value,
    });
  };

  const handleBlur = (e) => {
    setTouched({
      ...touched,
      [e.target.name]: true,
    });
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    const nextTouched = Object.keys(values).reduce((touched, field) => {
      touched[field] = true;
      return touched;
    }, {});
    setTouched(nextTouched);

    const errors = validate(values);
    setErrors(errors);
    if (Object.values(errors).some(Boolean)) return;

    onSubmit(values);
  };

  const getFieldProps = (name) => {
    const value = values[name];
    const onBlur = handleBlur;
    const onChange = handleChange;

    return {
      name,
      value,
      onBlur,
      onChange,
    };
  };

  React.useEffect(() => {
    setErrors(validate(values));
  }, [values]);

  return {
    values,
    errors,
    touched,
    handleBlur,
    handleChange,
    handleSubmit,
    getFieldProps,
  };
};

const formContext = React.createContext({});
formContext.displayName = "FormContext";

export const Form = ({ children, ...rest }) => {
  const formValue = useForm(rest);
  return (
    <formContext.Provider value={formValue}>
      <form noValidate onSubmit={formValue.handleSubmit}>
        {children}
      </form>
    </formContext.Provider>
  );
};

export const Field = ({ as = "input", children, ...rest }) => {
  const { getFieldProps } = React.useContext(formContext);
  return React.createElement(
    as,
    { ...rest, ...getFieldProps(rest.name) },
    children
  );
};

export const ErrorMessage = ({ name }) => {
  const { touched, errors } = React.useContext(formContext);
  if (!touched[name] || !errors[name]) return null;
  return <span>{errors[name]}</span>;
};

프로젝트

usePathname

회원가입, 로그인 페이지에서는 네비게이션 바가 보이지 않아야 하는 요구사항이 있다. 하지만 Next의 경우에도 layout에 보통 공통적으로 적용할 네비게이션 컴포넌트를 정의하기 때문에 일단은 로그인, 회원가입 페이지에서도 노출되는 게 일반적이다. 하지만 리액트 토이 프로젝트에서처럼 useLocation을 이용해서 현재 보여지는 페이지의 url 정보를 가지고 와서 로그인, 회원가입 페이지에서는 네비게이션 관련 컴포넌트를 숨길 수 있도록 하려고 했다.

Next에서 사용하는 훅은 대부분 리액트와는 조금씩 그 이름이 다르다. 사용성은 조금 비슷하다고 생각하지만 일단 useLocation으로는 가져올 수 없을 거라 생각했다. 처음에는 useRouter를 사용했다. 하지만 13버전 부터는 asPath라는 속성이 없어졌기 때문에 usePathname을 이용해서 url 값을 가지고 와야 했다.

그렇게 해서 현재 페이지 정보를 가지고 왔다. 그리고 현재 공통적으로 적용되는 layout에서 네비게이션 부분은 Header 컴포넌트에서 적용하고 있었기 때문에 그곳에서 path 정보가 로그인 페이지와 회원가입 페이지 일때는 null을 리턴할 수 있도록 해서 해결했다.

'use client';

import { usePathname } from 'next/navigation';

const Header: React.FC = () => {
  const path = usePathname();

  if (path === '/login' || path === '/register') return null;

  return (
    <header className="flex flex-row w-[100%] h-[10rem] text-[1.6rem]">
      ...
    </header>
  );
};

export default Header;
profile
데이터리터러시를 중요하게 생각하는 프론트엔드 개발자

0개의 댓글