React - React Hook

bkboy·2022년 7월 11일
0

웹 개발

목록 보기
15/26
post-thumbnail

React Hook (리액트 훅)

리액트 훅이란 리액트 16.8버전부터 추가된 기능으로, 클래스 컴포넌트와 생명주기 메서드를 이용하여 작업을 하던 기존 방식에서 벗어나 함수형 컴포넌트에서 더 직관적인 함수를 이용할 수 있게 하는 기능이다.

import React from "react";

class App extends React.Component {
  state = {
      number: 0
  };
  render() {
    return (
      <div style={{ "textAlign": "center" }}>
        <div style={{ "fontSize": "100px" }}>{this.state.number}</div>
        <button onClick={this.handleClickIncrement}>더하기</button>
        <button onClick={this.handleClickDecrement}>빼기</button>
      </div>
    );
  }
  handleClickIncrement = () => {
    this.setState(state => ({
      number: state.number + 1
    }));
  };
  handleClickDecrement = () => {
    this.setState(state => ({
      number: state.number - 1
    }));
  };
}

export default App;
import React, { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);
  return (
    <div style={{ textAlign: "center" }}>
      <div style={{ fontSize: "100px" }}>{number}</div>
      <button onClick={() => setNumber(number + 1)}>더하기</button>
      <button onClick={() => setNumber(number - 1)}>빼기</button>
    </div>
  );
};

export default App;

각각 클래스형 컴포넌트와 함수형 컴포넌트를 이용한 상태값을 제어하는 예제이다. 직관적으로 코드의 수가 많이 줄어들었고, this, 상속과 같은 어려운 개념이 없어 이해하기도 쉽다.

사용 규칙

  1. 훅은 최상위 레벨에서만 호출 가능하다. 반복문, 조건문, 중첩된 함수 내부에서 호출하면 안된다.
  2. 훅은 오직 리액트 함수 컴포넌트 내에서만 호출 가능하다.

리액트 내장 훅

useState

상태를 관리하는 훅 생명주기 메서드 constructor에서 상태를 초기화하는 것과 비슷한 역할을 한다. 초기 값을 받고, 반환 값으로 현재 상태와 상태를 설정하는 함수를 반환한다.

const [state, setState] = useState(initialState); 

useEffect

클래스 컴포넌트의 생명주기 메소드 componentDidMount, componentDidUpdate, componentWillUnmount를 통합한 것과 같은 것으로 화면에 렌더링이 완료된 후에 수행된다.

side effect가 발생하는 작업을 수행하는 훅으로 side effect는 React 컴포넌트가 화면에 렌더링된 이후에 비동기로 처리되어야 하는 부수적인 효과들을 일컫는다.
대표적인 예로 어떤 데이터를 가져오기 위해서 외부 API를 호출하는 경우, 일단 화면에 렌더링할 수 있는 것은 먼저 렌더링하고 실제 데이터는 비동기로 가져오는 것이 권장된다.

useEffect(() => {}); // 렌더링 결과가 실제 돔에 반영된 후마다 호출
useEffect(() => {}, []); // 컴포넌트가 처음 나타날때 한 번 호출
useEffect(() => {}, [의존성1, 의존성2, ..]); // 조건부 effect 발생, 의존성 중 하나가 변경된다면 effect는 항상 재생성됩니다.

useContext

Context는 리액트 컴포넌트 트리 안에서 전역적이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법이다.

A 컴포넌트에서 사용되는 상태값이 G 컴포넌트에 필요하지만 그 중간엔 필요하지 않아도 props로 넘겨받고 건네야하는 번거로움이 있다. 이를 해결해주는 것이 context이다. 전역 데이터를 context에 저장한 후, 데이터가 필요한 컴포넌트에서 해당 데이터를 불러와 사용할 수 있다.


리액트에서 context를 사용하기 위해서는 provider와 consumer를 사용해야한다.
provider는 생성한 context를 하위 컴포넌트에 전달하는 역할을 하고 concumner는 context의 변화를 감시하는 컴포넌트이다.

const themes = { dark: { background: 'black' }, light: { background: 'white' } } 

// context 객체 생성 
const ThemeContext = React.createContex(themes.dark);
const App = () => { 
  // value prop에 의해 Context의 현재 값이 결정된다.
	return ( <ThemeContext.Provider value={themes.light}> <Button /> </ThemeContext.Provider> ) 
} 
const Button = () => { 
    const contextValue = useContext(context); 
    return ( <button style={{background: contextValue.background}}> Button </button> ) 
}

useContext는 호출된 컴포넌트에서 가장 가까이에 위치한 provider의 props로 전달된 value 값에 따라 변경되고 그 값이 변할 때 useContext가 호출된 컴포넌트는 리렌더링 된다.

button에서 useContext가 호출되었고 가장 가까운 provider는 app에서 buttom을 감싸는 부분이다. 거기서 value prop에 의해 현재값이 결정이 되는 것이다.

컴포넌트가 3개인 코드도 봐보자.

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

ThemedButton에서 호출되었고 가장 가까운 provider는 App에 있고 그때의 value값이 theme의 값이 된다.
중간에 Toolbar를 거쳤지만 Toolbar에선 따로 props을 받아서 넘기는 작업이 없다.

useReducer

useState의 대체 함수로 컴포넌트 상태 업데이트 로직을 컴포넌트에서 분리 시킬 수 있다.

여기서 reducer는 현재 상태과 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수이고 useReducer는 이 reducer와 초기값, 상태 초기화 함수를 파라미터로 받고 현재 상태와 액션을 발생시키는 dispatch 함수를 반환한다.

아직 정확하게 이해가 안됐다. 좀 더 이해를 높힌 뒤 내용을 추가할 생각이다.

useRef

useRef는 특정 DOM을 선택할 때 주로 사용되며 .current 프로퍼티로 전달된 인자로 초기화된 변경가능한 ref 객체를 반환한다. 이 객체는 컴포넌트의 전 생애주기를 통해 유지된다.

const refContainer = useRef(null); 
// { current : null }의 형태의 객체를 반환한다.

이때 current의 값을 변경시켜도 리렌더링이 일어나지 않는다. 혹시 useState로 만든 다른 상태가 변화해서 리렌더링이 일어나도 ref 값은 초기화되지 않는다. (전 생애주기를 통해 값이 유지가 되기 때문이다!)

일반 변수는 값이 초기화 된다. 렌더링이라 함은 컴포넌트 즉, 함수가 다시 실행되는 것이기 때문이다.

DOM요소의 접근 하는 방법은 태크에 ref속성에 값을 넣어주면 된다.

const App = () => { 
    const inputRef = useRef(null); 
  
	useEffect(() => {
      	inputRef.current.focus();
    }, []);  
  
    return ( 
      <input ref={inputRef} type="text" placeholder="username"/> 
	) 
}

이렇게 하면 input태그에 접근해 인풋창을 클릭하지 않아도 입력할 수 있는 상태를 만들 수 있다.

useMemo

컴포넌트의 성능을 최적화시킬 수 있는 훅이다. Memoization 기법을 사용하는데 이는 기존에 수행한 연산의 결괏값을 어딘가에 저장해두고 동일한 입력이 들어오면 활용하는 기법이다.

useMemo는 값을 반환하며, 파라미터로 함수와 의존성 배열을 받는다. 이때 의존성 배열안에 있는 값이 변할 때 연산을 실행하고 그렇지 않다면 이전에 연산했던 결과를 계속해서 사용하는 것이다.

import React, { useState, useMemo } from 'react';

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState('');
  
  const getAverage = useMemo(() => {
    console.log('평균값 계산 중...');
    if (list.length === 0) return 0;
    const sum = list.reduce((a, b) => a + b);
    return sum / list.length;
  }, [list]); 

  const onChange = (e) => {
    setNumber(e.target.value);
  };

  const onInsert = () => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber('');
  };

  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값:</b> {getAverage} 
      </div>
    </div>
  );
};

리스트에 숫자를 추가하고 숫자가 추가될때만 평균값이 업데이트되는 코드이다. 만약 getAverage에 useMemo를 사용하지 않았다면 console창에 '평균값 계산 중..' 이라는 문구가 매 렌더링마다 올라왔을 것이다.

useCallback

useMemo와 마찬가지로 메모이제이션 기법을 이용해 성능을 최적화 시키는 훅이지만 그 대상이 함수로 바뀐다. 특정 함수를 새로 만들지 않고 재사용 가능하게 한다.

참고

profile
음악하는 개발자

0개의 댓글