[SeSACX코딩온] React - lifecycle, hook, router

JUHEE·2024년 3월 28일
0

SeSACX코딩온

목록 보기
16/26

LifeCycle

- 클래스형 컴포넌트의 기본기능이었지만 hook의 등장으로 함수형 컴포넌트에서도 사용이 가능해졌다
- 특정 컴포넌트가 렌더링되거나 업데이트, 제거될 때 이벤트를 발생시킬 수 있다
- useEffect()를 사용한다
- 3가지 상황: mount(생성), update(수정), unmount(제거)가 있다

Mount

- useEffect(()=>{},[])
- 콜백함수와 빈 배열을 받는 경우, mount될 때만 발생하는 내용을 설정할 수 있다
- useEffect(()=>{}) 배열을 받지 않는 경우에도 mount될 때 실행된다

update

- useEffect(()=>{}) 렌더링 될 때마다 실행된다
- useEffect(()=>{},[aaa, bbbb]) aaa, bbbb요소가 변경될 때 실행된다

unmount

- useEffect(()=>{
return() => {}
})
- return문이 반드시 존재해야한다. 컴포넌트가 사라질 때 실행될 내용이다

Hooks

- react의 새로운 기능으로 클래스 컴포넌트에서만 가능했던 state와 lifecycle이 가능하도록 돕는기능이다
- 앞서 배운 useState, useRef, useEffect도 hook의 한 종류이다
- hook은 최상위단계(맨위)에서만 호출이 가능하며, 함수형 컴포넌트 내부에서만 호출가능하다.
- 커스텀 훅 제작이 가능하며 이름은 use로 시작하는 것을 권장한다
- 수업에서는 6가지의 hook에 대해 다루었다 앞서 배운 3가지 외에도 useMemo, useCallback, useContext가 그것이다

useMemo

- 함수형 컴포넌트 내부에서 발생하는 연산을 최적화시켜주는 hook이다
- 렌더링 과정에서 특정값이 변경되었을 때만 연산을 실행한다
- 렌더링 될 때마다 실행되는 반복작업을 memo, 즉 미리 저장해서 다시 계산하지 않고 사용할 수 있다.
- useEffect()와 유사한 형태를 가진다 큰 차이점은 메모리에 저장하기 위한 return값이 반드시 존재한다는 것이다.
- useMemo를 사용하지 않을 경우 문제는 배열사용시에 더욱 두드러진다
- 예를 들어 아래와 같은 배열을 location에 저장하는 1번 경우

const location = {
  country: isKorea ? "한국" : "외국",
};

- useMemo로 데이터를 저장하는 2번경우

const location = useMemo(() => {
  return {
    country: isKorea ? "한국" : "외국",
  };
}, [isKorea]);

=> 두 경우 모두 isKorea의 값에 따라 한국 또는 외국이라는 문자열을 저장하는 배열을 저장하고 있다. 렌더링될 때 컴포넌트가 다시 읽히게 되면 배열은 각각 다른 주소에 계속해서 저장되고, 비교할 때는 주소값을 기준으로 비교하기 때문에 1번의 경우 전혀 상관없는 input창의 onChange이벤트로 렌더링 작업이 일어나도 문자열이 변경되는 문제가 발생한다. useMemo는 이러한 문제를 막을 수 있는 것이다.
- 다만 useMemo는 메모리 과부화를 불러올 수 있어 꼭 필요한 경우가 아니라면 지양하는 것이 좋다

useCallback

- rendering 최적화에 사용되는 hook Api로 useMemo와 유사하다.
- useMemo는 값을 최적화한다면 useCallback은 rendering시 함수를 다시 불러오는 것을 막는다
- ppt에 명확하게 설명되어있다

useReducer

- 현재 상태와 업데이트를 위해 필요한 정보를 담은 액션값을 전달받아 새로운 상태를 반환하는 함수이다
- useState와 비슷한 작업을 하는데, 복잡한 state를 다룰 때 사용한다
- 네가지 요소: state, dispatch, action, reducer가 있다
- dispatch: dispatch 안에 ({배열}) 형태로 action을 전달
- reducer: 함수형태로 위에 선언하고 (prevState, action) state값과 action값을 받아 작동(setState개념으로 보면됨)
=> 사용은 dispatch 실제 작동은 reducer에서 한다
- 선언: const [state, dispatch] = useReducer(reducer, initialState)
- 상황에 따라 useState와 useReducer를 유동적으로 선택하여 사용해야한다

  • useReducer사용예시
    - 입금, 출금 작업 후 잔고를 표시하는 코드를 작성
    1) useReducer선언
const [money, dispatch] = useReducer(reducer, 0);

2) onClick이벤트 발생 시 dispatch 작동 설정, action전달

<button onClick={() => dispatch({ type: "deposit", payload: number })}>
  예금
</button>
<button onClick={() => dispatch({ type: "withdraw", payload: number })}>
  출금
</button>

3) reducer함수 선언: type, payload는 배열형태 action.으로 접근
아래 코드에서는 type에 따른 switch문으로 각각의 결과를 return, return값이 곧 새로운 money state값이 된다

const reducer = (prevState, action) => {
  //action: state를 어떻게 바꿀지에 대한 요구
  //dispatch: action을 담아서 reducer로 전달
  console.log("dispatch가 호출되면 reducer동작");
  console.log(action);

  //reducer내부의 로직은 switch, if등의 조건문을 많이 씀
  switch (action.type) {
    case "deposit":
      return prevState + action.payload;
    case "withdraw":
      return prevState - action.payload;
  }
};

custom hook

- 함수와 같다, 자주 사용되는 함수에서 hook을 사용한다면 use + 나만의 이름을 짓고 import하여 사용해준다
- const [value, setValue] = use+hook이름(초기값);
- 보통 hooks 디렉토리를 생성하고 한곳에 모아서 관리한다

useForm

- react에서 form의 상태관리와 유효성검사를 돕는다
- npm install react-hook-form 설치가 필요하다
- 중요한 기본 제공 요소로는 register, handleSubmit, watch, formState가 있는데 수업에서는 해당 요소들을 객체분할할당하여 곧바로 사용했다 따라서 선언은 다음과 같았다

const {
  register,
  handleSubmit,
  watch,
  formState: { errors },
} = useForm();

register

- form의 name을 설정하고, 유효성검사 규칙을 설정할 수 있다
- register("name", {유효성규칙})으로 사용되는데 강의에서는 ...전개연산자를 통해 input요소에 register함수를 곧바로 사용했다

<input
  type="text"
  placeholder="username"
  {...register("username", {
    required: "이름을 입력해주세요",
    minLength: {
      value: 2,
      message: "이름은 최소 두글자 이상 작성해주세요",
    },
  })}
/>

handleSubmit

- form의 제출을 처리하는 함수로 두개의 인자(유효작동, 비유효작동)를 받아 정상적인 제출이 되었을 때와 그렇지 않았을 때의 작동을 구분하여 설정할 수 있다
- 두번째인자는 선택사항이다
- data는 배열 형태로 form요소의 name과 입력값을 전달받으며 submit성공시 axios요청을 실행하면된다

<form onSubmit={handleSubmit(onValid, onInValid)}>
  
const onValid = (data) => {
    console.log("valid", data);
    // data = {username: '~~'}
    //axios요청 가능
};
const onInValid = (data) => {
    // register의 두번째 인자에 대한 정보가 온다
    console.log("invalid", data);
    // console.log(data.username?.message); //있으면 메시지 출력
};

watch

- 특정 form요소의 값을 실시간으로 관찰하는 함수이다
- watch(name)으로 곧바로 접근할 수 있다. ()인자를 생략할 경우 모든 요소를 객체형태로 반환한다

formState

- form의 상태를 나타내는 객체로 수업에서 사용된 errors외에도 isValid, isDirty, isSubmitted 등의 상태를 포함한다
- 수업에서는 input창 아래에서 설정한 register의 유효성 검사를 통과하지 못했을 경우 메시지를 출력하도록 아래와 같이 설정했다

<input
  type="text"
  placeholder="username"
  {...register("username", {
    required: "이름을 입력해주세요",
    minLength: {
      value: 2,
      message: "이름은 최소 두글자 이상 작성해주세요",
    },
  })}
/>
{errors.username?.message}

=> 이름을 공백으로 넘기면 "이름을 입력해주세요", 두글자 이하로 입력하면 "이름은 최소 두글자 이상 작성해주세요" 메시지가 표시된다
?를 통해 존재할 경우에만 message가 보여지도록 했다

Router

- react는 single page application으로 단일 웹페이지에서 컴포넌트를 동적으로 보여주는 방식이다
- 라우팅을 통해서 특정 주소로 접근하면 컴포넌트가 보여지도록 설정할 수 있다
- npm install react-router-dom@6로 6버전을 설치했다

BrowserRouter

- Router의 역할
- 새로고침없이 새로운 컴포넌트를 렌더링하는 기능을 담당하며, 바뀌는 부분의 최상단에 위치해야한다
- index.js파일에서 App컴포넌트를 import하는 코드를 감싸도록 import 한다

Routes, Route

- app.js파일에서 사용한다
- Routes는 여러개의 Route를 감싸고 있다. Route에는 경로가 될 path와 렌더링할 컴포넌트 element를 적는다

- 기존에는 a태그를 이용하여 페이지를 이동할 수 있었다. react역시 a태그 사용이 가능하지만 link태그를 사용하면 새로고침없이 부드럽게 페이지를 이동할 수 있다
- a href => link to 속성에 주소를 입력한다
- navigate는 link컴포넌트를 사용하지 않고, 뒤로가기 등의 기능을 가능하게 한다
- 선언: const navigate = useNavigate() import한다
- onClick={()=>navigate(-1)}: -1 즉, 클릭시 이전페이지로 이동하도록 한다

Parameter

- 선언: Route의 path주소는 이전 parameter사용과 마찬가지로 /:parametername으로 적는다
- 사용: useParams()를 통해 해당 주소로 렌더되는 컴포넌트에서 받아 사용할 수 있다
- 예시코드
1) ProductDetailPage는 /products/:productId의 주소로 productId하는 parameter를 받는다

<Route
 path="/products/:productId"
 element={<ProductDetailPage products={products} />}
/>

2) ProductItem 컴포넌트에서 특정 상품을 클릭하면 Link태그로 ${product.id}파라미터를 /products/:productId로 전달한다

export default function ProductItem({ product }) {
  return (
    <Link className="ProductItem" to={`/products/${product.id}`}>
      <ul>
        <li>상품명: {product.name}</li>
        <li>상세설명: {product.body.slice(0, 80)}</li>
      </ul>
    </Link>
  );
}

3) ProductDetailPage는 객체분해할당으로 params로 전달된 productId를 받고, props로 전달받은 products 객체에 filter함수를 사용하여 id에 해당하는 targetProduct를 찾는다

export default function ProductDetailPage({ products }) {
	const { productId } = useParams();
  	const [targetProduct] = products.filter(
    (product) => Number(productId) === product.id
  );
}

Query

- 쿼리 사용시에는 Route의 path에서 따로 설정해 줄 필요는 없다(params와 다른점)
- useSearchParams()로 사용할 수 있다
- 선언: useSearchParams를 import하여 선언한다

const [modeParams, setModeParams] = useSearchParams();

- 설정: set함수를 사용한다(직접설정)

<button
  onClick={() => {
    setModeParams({ mode: "dark" });
  }}
  >
  Dark mode
</button>
//아래와 같이 백틱을 사용한 설정도 가능
setModeParams(`mode=dark`);
//클릭시 주소는 http://localhost:3000/?mode=dark

- 사용: get함수를 사용한다(기본제공)

//modeParams.get("쿼리이름")
console.log(modeParams.get("mode")); //dark

추가로 알아두기: axios요청

- react axios요청은 npm 모듈을 다운받아 사용했다
- npm install axios
- axios.get("url")을 입력하면 해당 url로 get요청을 보내 데이터를 받아올 수 있다
- .then이 아닌 async/await사용시 주의할 점이 있다
- 보통 컴포넌트가 mount될 때 데이터를 가져오기 때문에 useEffect()안에 axios를 사용하는데 useEffect는 async/await를 지원하지 않는다. 때문에
1) 그냥 then절로 response받는 방법
2) async/await를 사용하는 axios실행 함수를 만들고, useEffect에서는 해당함수를 불러오도록 하는 방법을 사용한다
- 수업에서는 products 더미 데이터를 받아오기 위해 get요청을 보내는 getProducts함수를 작성하고 해당 response데이터를 미리 설정한 state에 저장한 후 useEffect(()=>{},[])를 통해 mount될 때 getProducts함수가 실행되도록 설정했다

profile
초보개발자

0개의 댓글