리액트 : Hooks

지환·2023년 12월 16일
0

리액트

목록 보기
5/8
post-thumbnail

  1. 함수Component가 Class Component의 기능을 동일하게 구현할 수 있도록 등장한 것이 Hooks이다.

1, 앞에 use를 붙여서 Hooks 기능임을 표시한다.

UseState()

  1. state라는 것을 사용하기 위한 Hook이다.

  2. 함수 컴포넌트에서는 기본적으로 State라는 것을 제공하지 않기 때문에 class 컴포넌트처럼 state를 사용하고 싶으면 userState hook를 사용해야한다.

import React from "react";

function Counter(props) {
  let count = 0;
  return (
    <div>
      <p>{count}번을 클릭했습니다.</p>
      <button onClick={() => count++}>클릭</button>
    </div>
  );
}

export default Counter;

카운트를 함수의 변수로 선언해서 사용하게 되면 클릭 시 카운트 값을 증가시킬 수는 있지만, 재렌더링이 일어나지 않아 새로운 카운터 값이 화면에 표시되지 않는다.

이럴 땐, State를 사용해서 값이 바뀔 때 마다 재렌더링 되도록 해야 하는데, 함수 컴포넌트에는 해당 기능이 따로 없어서 useState 를 이용해야함

  • useState를 노출할 때는 파라미터로 선언할 state 초기값이 들어간다.

    • 클래스 컴포넌트의 생성자에서 state를 선언할 때, 초기값을 넣어주는 것과 동일하다.
      [클래스 컴포넌트]
      class NotificationList extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            notifications: [],
          }; // 생성자에서 앞으로 사용할 데이터를 state에 넣어서 초기화한다.
        }
  • 이렇게 호출하면 return으로 배열이 나오는데, 배열에는 두 가지 항목이 나온다.

    • 첫 번째 : state로 선언된 변수

    • 두 번쨰 : state의 set함수이다.

import React from "react";
import { useState } from "react";

function Counter(props) {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>{count}번을 클릭했습니다.</p>
      <button onClick={() => setCount(count+1)}>클릭</button>
    </div>
  );
}

export default Counter;
  • 버튼이 눌렸을 떄, setCount를 호출해서 카운트 1증가시킨다.

  • 카운터의 값이 변경되면 컴포넌트가 재렌더링 되면서 화면에 새로운 카운트 값이 표시된다.

  • 클래스 컴포넌트에서 setState 함수를 호출해서 state가 업데이트 되고 컴포넌트가 재랜더링되는것이랑 동일하다.

클래스 컴포넌트에서는 setState함수 하나를 사용해서 모든 State의 값을 관리했지만, useState에서는 변수 각각에 대해 set함수가 따로 존재한다.

useEffect() 사용법

  • useEffect(이펙트함수, 의존성배열);

    • 의존성 배열이란 ?

      • 이 이펙트가 의존"하고 있는" 배열인데, 배열 안에 있는 변수중에 하나라도 값이 변경되었을 떄 이펙트 함수가 실행된다.
    • Effect function이 mount, unmount시에 단 한 번씩만 실행된다.

      • Effect function이 한 번만 일어나게 하고 싶으면,
        useEffect(이팩트함수,[]); 로 정의하면 된다.
        • 이렇게 하면 해당 이펙트가 props나 state에 있는 어떤 값에도 의존하지 않는 것이 되므로 여러번 실행되지 않는다.

[예제]

import React, { useState, useEffect } from "react";

function Counter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>{count}번 실행된다.</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      ></button>
    </div>
  );
}
export default Counter;

  • useEffect() : componentDidMount, componentDidUpdate와 비슷하게 작동한다.

  • 의존성 배열 없이 useEffect를 사용하면 React는 DOM이 변경된 이후에 해당 이펙트 함수를 실행하라는 의미로 받아들인다.

    • 기본적으로 컴포넌트가 처음 렌더링 될 때를 포함해서 매번 렌더링 될 때 마다 이펙트가 발생한다.

    • 이 코드의 경우 이펙트 함수 + 처음 컴포넌트가 마운트 되었을 떄 실행되고 이후 컴포넌트가 업데이트 될 때마다 실행된다.

    • 결과적으로 DidMount + DidUpdate 동일한 역할을 진행한다.

    • 이펙트는 함수 컴포넌트 안에서 선언되기 때문에 컴포넌트의 props와 state에 접근할 수 있다.

      • 이 코드에선 count라는 state에 접근하여 값이 포함된 문자열을 생성해서 사용한다.

[구독 example (참고)]

import React, { useState, useEffect } from "react";

function UserStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
 
  function handleStatusChange(status)
 {
    setIsOnline(status.isOnline);
 }
 useEffect(()=>{
   // 구독
    ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange); 
    return ( // 컴포넌트가 unmount 될 떄 호출된다.
        ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
    // useEffect에서 리턴하는 함수는 컴포넌트가 mount 해제, unmount 될 떄, 
        );
 });
 if(isOnline === null)
 {
    return `대기중....`;
 }
 return isOnline ? `온라인` : `오프라인`;
}


export default UserStatus;

[내가 해석한 소스리뷰]

  • useEffect는 상태가 변경되면 실행된다. 현재, 구독시에 useEffect()가 실행되는데,

  • ServerAPI.subscribeUserStatus는 파라미터로 2개를 받는데,

    • props.user.id : userStatus의 props

    • handleStatusChange : 함수호출(콜백)을 실행한다.

      • handleStatusChange를 실행하여 -> setIsOnline(status.isOnline)을 주입한다.

      • const[status.isOnline]으로 상태값 변수를 받는다.

    • 그 상태값으로 isOnline === null이면 --> return 처리하고

  • 그게 아니라면 삼항연산자를 통해서 리턴한다.

  • return ServerAPI.unsubscribeUserStatus 하는 경우

    • 이 떄는, useEffect에서 unmount 할 때 사용한다. 동작원리는 위 설명과 같다.
    • return함수의 역할은 Java에서만 하다가 생소한 개념인데, return () => 함수로 본다. (신기하네..)
      • componentWillUnmount 역할을한다.

    componentWillUnmount란 무엇인가?

  componentWillUnmount() {
    // 컴포넌트가 언마운트되기 전에 수행되는 코드
  }

만약에 의존성 배열을 추가한다면?

useEffect(() => {
  // 구독
  ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);

  // 언마운트 시 정리
  return () => {
    ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
  };
}, [props.user.id]); 
import React, { useState, useEffect } from "react";

const UserStatusWithCounter = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(()=>{
    ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
    return ( // 컴포넌트가 unmount 될 떄 호출된다.
        ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
    // useEffect에서 리턴하는 함수는 컴포넌트가 mount 해제, unmount 될 떄, 
        );
 });

 function handleStatusChange(status)
 {
    setIsOnline(status.isOnline);
 }
  
};

export default UserStatusWithCounter;

  • 두 개의 useEffectHook를 사용할 수 있다.

강의내용끝=======


UseState 정복하기

출처 | https://velog.io/@velopert/react-hooks#11-usestate-%EB%A5%BC-%EC%97%AC%EB%9F%AC%EB%B2%88-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0 해당 블로그를 정리한 내용입니다.

import React, { useState } from "react";

const Counter = () => {
  const [value, setValue] = useState(0);
  return (
    <div>
      <p>
        현재 카운터 값은 <b>{value}</b>입니다.
      </p>
      <button onClick={() => setValue(value + 1)}></button>
      {/* 콜백함수 클릭 됐을 떄, 처리 */}
      <button onClick={() => setValue(value - 1)}></button>
    </div>
  );
};

export default Counter;

비할당구조 예제

	const [value, setValue] = useState(0);

	const array = ['dog','cat','sheep'];
	const [first,second] = array;
	console.log(first,second);
  • 함수의 파라미터에는 상태의 기본값을 넣는다. useState(0)
  • 함수가 호출되고 나면 배열을 반환한다.
    • 배열의 첫 번째 원소는 상태값
    • 배열의 두 번쨰 원소는 상태를 설정하는 함수다.
  • 이 함수에 파라미터를 넣어서 호출하게 되면, 전달받은 파라미터로 값이 바뀌게 되고 컴포넌트는 정상적으로 리렌더링된다.

렌더링해보자.

index.jsx

import React, { Fragment } from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import Counter from "./chapter_07/Counter";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Counter />
  </React.StrictMode>,
  document.getElementById("root")
);

1.1 useState 를 여러번 사용하기

  • 하나의 useState 함수는 하나의 상태 값만 관리할 수 있다. 컴포넌트에서 관리해야 할 상태가 여러개라면 useState를 여러번 사용하면 된다.
import React, { useState } from "react";

const Info = () => {
  const [name, setName] = useState("");
  const [nickname, setNickname] = useState("");

  const onChangeName = (e) => {
    setName(e.target.value);
    // setter와 같은원리 -> setName함수를 호출하여 name값에 e.tatget.value에 꽂는다.
  };

  const onChangeNickname = (e) => {
    setNickname(e.target.value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={onChangeName} />
        <input value={nickname} onChange={onChangeNickname} />
      </div>

      {/* onChange --> change 될때, onChangeNmae함수가 호출된다. 
  사용자가 입력할 떄 마다 호출되며, 입력값이 name, nickname 변수에 꽂힌다.

*/}

      <div>
        <div>
          <b>이름 :</b> {name}
        </div>
      </div>

      <div>
        <div>
          <b>닉네임 :</b> {nickname}
        </div>
      </div>
    </div>
  );
};

export default Info;

index.js에 렌더링함

useEffect

  1. useEffect는 리액트 컴포넌트가 렌더링 될 때마다, 특정 작업을 수행하도록 설정 할 수 있는 Hook이다.
  1. 클래스형 컴포넌트의 ComponentDidMountComponentDidUpdate 를 합친 형태로 보아도 무방하다.

  2. 우리가 만든 Info 컴포넌트에 useEffect를 적용해보자.

import React, { useState } from "react";

const Info = () => {
  const [name, setName] = useState("");
  const [nickname, setNickname] = useState("");

  const onChangeName = (e) => {
    setName(e.target.value);
    // setter와 같은원리 -> setName함수를 호출하여 name값에 e.tatget.value에 꽂는다.
  };

  const onChangeNickname = (e) => {
    setNickname(e.target.value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={onChangeName} />
        <input value={nickname} onChange={onChangeNickname} />
      </div>

      {/* onChange --> change 될때, onChangeNmae함수가 호출된다. 
  사용자가 입력할 떄 마다 호출되며, 입력값이 name, nickname 변수에 꽂힌다.

*/}

      <div>
        <div>
          <b>이름 :</b> {name}
        </div>
      </div>

      <div>
        <div>
          <b>닉네임 :</b> {nickname}
        </div>
      </div>
    </div>
  );
};

export default Info;

기존 코드에서

  useEffect(() => {
    console.log("렌더링이 완료되었습니다!");
    console.log({
      name,
      nickname,
    });
  });

이 부분이 추가되었다.

2.1 마운트 될 때만 실행하고 싶을 때

  1. UseEffect에서 설정한 함수가 컴포넌트 화면에 가장 처음 렌더링 될 때만 실행되고 업데이트할 경우에는 실행 할 필요가 없는 경우엔 함수의 두 번째 파라미터로 비어있는 배열을 넣으면 된다.
  useEffect(() => {
    console.log("렌더링이 완료되었습니다!");
    console.log({
      name,
      nickname,
    });
  }, []);

배열을 넣으면 된다.

  • 컴포넌트가 처음 나타날 때만 콘솔에 문구가 나타나고 그 이후에는 나타나지 않는다.

특정 값이 업데이트 될 때만 실행하고 싶을 때,

  • useEffect를 사용 할 때 특정 값이 변경이 될 때만 호출하게 하고 싶을 경우도 있다.

클래스형 컴포넌트라면 어떻게 작성할까?

componentDidUpdate(prevProps, prevState)
{
	if(prevProps.value !== this.props.value)
    {
    	doSomething();
    }
}
  • props 안에 들어있는 value의 값이 바뀔때에 특정 작업을 수행하도록 되어있다.

  • 만약 이런 작업을 useEffect에서 해야 한다면 어떻게 해야될까?

useEffect두 번쨰 파라미터로 전달되는 배열안에 검사하고 싶은 값을 넣어주면 된다.

Info.jsx - useEffect


useEffect(()=>{
	console.log(name);
},[name]);
  • [name] : 배열 안에는 useState를 통해 관리하고 있는 상태를 넣어주면 된다.

  • props로 전달받은 값을 넣어주어도 된다.

2.3 뒷정리 하기

  • useEffect는 기본적으로 렌더링 되고난 직후마다 실행된다. 두번째 파라미터 배열에 무엇을 넣느냐에 따라 실행되는 조건이 달라진다.

  • 컴포넌트가 언마운트 되기 전이나, 업데이트 되기 직전에 어떠한 작업을 수행하고 싶다면, useEffect에서 뒷정리 함수를 반환해주어야 한다.

    • 마운트 : 처음 나타났을 떄,

    • 언마운트 : 사라졌을 떄,

Info.jsx 추가

  useEffect(() => {
    console.log('effect');
    console.log(name);
    return () => {
      console.log('cleanup');
      console.log(name);
    };
  });

App.jsx에 추가

import React, { useState } from 'react';
import Info from './Info';

const App = () => {
  const [visible, setVisible] = useState(false);
  return (
    <div>
      <button
        onClick={() => {
          setVisible(!visible);
        }}
      >
        {visible ? '숨기기' : '보이기'}
      </button>
      <hr />
      {visible && <Info />}
    </div>
  );
};

export default App;

[내가 해석한 소스리뷰]

  1. 보이기를 클릭 했을 때, Info 컴포넌트를 렌더링하여 보여준다.
    {visible ? '숨기기' : '보이기'} 삼항연산자로 숨기기와 보이기를 둘 중에 하나를 클릭한 결과를 visible에 담는다.

  2. {visible && <Info />} 이 부분은 보이기를 클릭했을 때,

  • '보이기' && <Info/>로 해석할 수 있다. Info부분을 화면에 렌더링함.
  1. 숨기기를 누르는 순간 effect-cleanup가 발생한다.

[몰랐던 부분]

  • setVisible(!visible); 를 통해서 visible 상태가 토글된다.

  • {visible && } : visible이 true일 떄만 를 렌더링한다.

  • 숨기기 버튼을 클릭하면 , 컴포넌트가 언마운트된다. + 이 떄 useEffect의 cleanUp 부분이 실행된다.

  • useState: 함수 컴포넌트에서 상태를 관리하는데 사용한다.

    • const[visible, setVisible] = useState(false); : visible상태와 상태를 업데이트 하는 setVisible 함수를 생성한다.
  • useEffect는 "부수효과"를 다루기 위해 사용된다.

    • 컴포넌트가 렌더링 될 때, visible 상태가 변경될 때, "useEffect"가 실행된다. ==> "렌더링이 완료되었습니다" 라고 상태 정보를 콘솔에 출력한다.

[결과]

컴포넌트가 나타날 떄 콘솔에 effect가 보이고, 사라질 때, cleanUp이 보여지게 된다.

한 번 인풋에 이름을 적어보고 어떤 결과가 나타나는지 확인해보자.

렌더링이 될 떄 마다, 뒷정리 함수가 계속 보여지고 있다. 그리고 뒷정리함수가 호출될 때에는 업데이트 되기 직전의 값을 보여주고 있다.

만약에 언마운트 될 때만 함수를 호출하고 싶다면, useEffect함수의 두 번쨰 파라미터에 비어있는 배열을 넣으면 된다.

Info.jsx - useEffect

useEffect(()=>{
	console.log('effect');
  	console.log(name);
  	return () => {
    	console.log('cleanup');
      	console.log(name);
    };

}, []);

3. useContext

  • 함수형 컴포넌트에서 Context를 쉽게 사용할 수 있다.

  • src 디렉토리에 ContextSample.jsx라는 컴포넌트를 만들어보자.

import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('black');
const ContextSample = () => {
  const theme = useContext(ThemeContext);
  const style = {
    width: '24px',
    height: '24px',
    background: theme
  };
  return <div style={style} />;
};

export default ContextSample;

다 만들었다면 App 컴포넌트에서 렌더링을 진행하자.

import React from 'react';
import ContextSample from './ContextSample';

const App = () => {
  return <ContextSample />;
};

export default App;

profile
아는만큼보인다.

0개의 댓글