[TIL][React] 함수형 컴포넌트와 Hooks

ddoni·2021년 1월 23일
1

Function Components

함수형 컴포넌트는 자바스크립트 함수로 정의된 리액트 컴포넌트이다. 함수형 컴포넌트는 반드시 JSX를 리턴해야한다.

  1. 개념

리액트의 컴포넌트는 클래스 또는 함수로 정의 가능하다. 클래스 컴포넌트 내에는 반드시 render() 를 써야했지만 함수 컴포넌트에선 안써줘도 된다.

// A component class written in the usual way:
export class MyComponentClass extends React.Component {
  render() {
    return <h1>Hello world</h1>;
  }
}

// The same component class, written as a stateless functional component:
export const MyComponentClass = () => {
  return <h1>Hello world</h1>;
}

// Works the same either way:
ReactDOM.render(
	<MyComponentClass />,
	document.getElementById('app')
);
  1. props

클래스 컴포넌트와 마찬가지로 함수 컴포넌트에도 props를 통해 정보 전달이 가능하다. props에 접근하기 위해서는 함수 컴포넌트의 매개변수 이름으로 props를 넣어준다. 함수 바디 내에서는 props.propertyName 으로 사용해준다(this 키워드는 없어도 된다!)

export function YesNoQuestion (props) {
  return (
    <div>
      <p>{props.prompt}</p>
      <input value="Yes" />
      <input value="No" />
    </div>
  );
}
 
ReactDOM.render(
  <YesNoQuestion prompt="Have you eaten an apple today?" />,
  document.getElementById('app');
);

Hooks

  1. 개념

클래스는 컴포넌트 간 재사용이 어렵고, 테스트하는데에 시간이 소요되고, 많은 개발자들을 혼란스럽게 하고 많은 버그들을 일으킨다. 이를 보안하기 위해 Hooks가 도입되었다. 리액트 Hooks는 함수로 state를 연결(hook into)하고 바로 함수 컴포넌트에 라이프사이클 기능을 연결 할 수 있게 해준다.(Hooks는 클래스 내에선 작동 안된다.)

  1. State Hook

리액트 컴포넌트들을 만들기 위해 가장 많이 사용되는 Hook이다. 사용하기 위해선 import 해줘야한다.

//기본 react-library import와 그중 useState를 사용한다라고 선언
import React, { useState } from 'react';

useStae() 는 리액트 라이브러리에서 정의된 자바스크립트 함수이다. 이 함수를 호출하게 되면, 두개의 값을 가진 배열을 리턴한다. 배열 형태로 리턴되기 때문에 지역변수에 할당하여 사용할 수 있다.

  • current state : 지금 상태의 현재 값 - state setter : 지금 상태의 값을 업데이트 하기 위해 사용할 수 있는 함수
//State Hook 을 사용한 함수 컴포넌트

import React, { useState } from "react";             State Hook 을 사용하기 위한 import
 
function Toggle() {
  const [toggle, setToggle] = useState();    useState()가 리턴하는 값을 디스트럭쳐링 방식으로 변수에 할당
 
  return (
    <div>
      <p>The toggle is {toggle}</p>
//state setter 함수인 setToggle은 현재값을 나타내는 변수 toggle의 값을 업데이트하고
//새로운 상태값으로 컴포넌트를 리렌더하기 위해 호출된다.
      <button onClick={() => setToggle("On")}>On</button>
      <button onClick={() => setToggle("Off")}>Off</button>
    </div>
  );
}

✨ 클래스 컴포넌트 내에서 상태 값을 변경하기 위해 번거로운 작업들이 함수 컴포넌트에선 useState()를 사용하는 것으로 해결된다.

  1. Initialize State

함수 컴포넌트가 렌더되어 질때 useState() 에 최초의 값이 없으면 undefined 가 리턴된다.(상태값이 필요한데 없는 경우, 처음렌더시 어떠한 값도 필요없으면 useState() 인자로 null이라고 전달해줘야한다.)

//아래와 같은 ToggleLoading 함수 컴포넌트가 있을때,
//렌더 될때 초기 상태 값을 원하는 값으로 넣을 수 있다 -> useState()의 인자로 넣기
import React, { useState } from 'react';
 
function ToggleLoading() {
//처음 렌더될때의 상태값은 true로 세팅되어 있다.
  const [isLoading, setIsLoading] = useState(true);
 
  return (
    <div>
      <p>The data is {isLoading ? 'Loading' : 'Not Loading'}</p>
      <button onClick={() => setIsLoading(true)}>
        Turn Loading On
      </button>
      <button onClick={() => setIsLoading(false)}>
        Turn Loading Off
      </button>
    </div>
  );
}
  1. set from previous state

이전 상태 값을 이용하여 업데이트 될 상태값에 계산이 필요한 경우 callback 함수를 사용하여 준다.

//state hook 을 사용하기 위하여 import
import React, {useState} from 'react'

export default Counter = () => {
//현재 상태 값과 상태 업데이트 함수 선언
	const [count, setCount] = useState(0);

	const increment = () => {
		setCount(prev => prev += 1;);
	}

	return (
		<div>
			<p>
				You've clicked {count} times!
			</p>
//버튼이 클릭될때마다 increment 클릭이벤트 함수가 실행됨
			<button onClick={increment}></button>
		</div>
	)
}

✨ state setter 가 콜백함수를 호출하면, state setter 콜백함수는 이전 상태 값을 인자로써 받는다.

  1. Arrays in state
const toggleTopping = ({target}) => {
    const clickedTopping = target.value;
    setSelected((prev) => {
     // check if clicked topping is already selected
      if (prev.includes(clickedTopping)) {
        // filter the clicked topping out of state
        return prev.filter(t => t !== clickedTopping);
      } else {
        // add the clicked topping to our state
        return [clickedTopping, ...prev];
      }
    });
  };
export default function GroceryCart() {
//array형태의 상태값 관리가 필요한경우 initailize 단계에서 [] 설정해준다.
  const [cart, setCart] = useState([]);

//배열에 아이템 추가하기
  const addItem = (item) => {
//setter state함수에서 콜백함수를 실행하여 이전상태값을 불러와 아이템을 추가한다.
    setCart((prev) =>
    [...prev, item])
   };

//배열에 아이템 삭제하기
  const removeItem = (targetIndex) => {
//setter state 함수에서 이전 상태값을 불러와서 인자로 전달되는 것을 filter로 찾아낸다
    setCart((prev) => prev.filter((item, idx) => idx !== targetIndex))
  };

✨ 아이템이 추가 되는 함수에선 이전 상태값에 추가되는 것이기 때문에 spread syntax로 이전 상태 배열의 모든요소를 불러와서 추가한다.

✨ 아이템 삭제 함수에선 filter 메소드를 이용하여 타겟인덱스와 상태 데이터의 인덱스와 일치하는 것을 찾았는데 item 이름으로 접근하게 되면 중복된 데이터가 있는 경우 모두 필터링 된다. filter 메소드는 idx를 전달해주기 때문에 이를 유니크한 식별자로써 사용하여 필터링 가능하다.

  1. Objects in state

배열 데이터 상태 관리와 동일하게 initialize 단계에서 빈객체를 넘겨주고 이전 상태과 비교하여 업데이트 할게 있으면 setter state 함수내에서 콜백함수를 실행한다.

const handleChange = ({ target }) => {
    const { name, value } = target;
    setFormState((prev) => ({
      ...prev,
      [name]: value
    }));
  };

✨ 객체 데이터 타입을 spread syntax로 표현할때 괄호로 묶어주는데 이는 자바스크립트에게 새로운 객체게 리턴될것이라고 말해주는 것이다.

  1. Seperate Hooks for Seperate States

원하는 만큼 useState()를 호출 할 수 있으므로 값이 함께 바뀌는 것에 기반하여 수많은 상태 변수들을 분리해주는 것이 좋은 방법이다.(한번에 useState() 내에 선언해주는 것은 관리하기 어려움!)

0개의 댓글