tech.junha.kr

[React Hook 시리즈 1]: 쓰는 이유, useState()

1. React hook 이란?

함수형 컴포넌트는 컴포넌트 내부의 state를 만들 수 없고, 컴포넌트의 생성, 변경, 소멸에 대한 이벤트인 라이프사이클 API를 사용할 수 없었다. 그저 상위 컴포넌트가 시키는 일만 처리하는 단순한 컴포넌트에만 사용됐다. 하지만 16.8 버전부터 훅(hook)이라는 개념이 도입되며, 이 둘을 해결할 수 있게 됐다. 두 가지 기능을 함수형 컴포넌트에 "hook into”, 끌어와 연결 한다고 해서 "hook"이라고 부른다. 또한 클래스 컴포넌트에서 귀찮게 사용되는 didMount, render, this 등 대신, 함수형 프로그래밍 스타일로 가독성 있게 코드를 작성할 수 있게 됐다.

Class Component

import { Component } from 'react';

class Example extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

Functional Component using hooks

import { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • 함수형 컴포넌트의 코드가 훨씬 간결해지고, 가독성이 좋다.

  • 하지만 Facebook이 Class Component를 꾸준히 지원한다고 약속했으니, 두 가지 방법 모두 알아두자

함수형 컴포넌트의 첫 번째 파라미터는 props값을 전달받는다.

function FuncComp(props){
  return(<h1>{props.count}</h1>)
}

function FuncComp({count}){
  return(<h1>{count}</h1>)
}

2. useState

  • default 값 없이 useState()를 작성하면, state값은 undefined가 된다.
import {useState} from 'react'

function FuncComp(props){
  
  var countState = useState(props.count)
  // useState의 반환값: 배열
  // useState(initValue)
  
  var count = countState[0]              // 첫 번째 값: state 값
  var setCount = countState[1]           // 두 번째 값: state를 바꾸는 함수
  
  return(
    <h1>{count}</h1>
    <button onClick={()=>setCount(count+1)}>
  )
}

최상위 레벨에서만 훅을 사용할 수 있다.

-hook 은 for문, if문, 중첩 함수 내에서 호출 될 수 없다.

import {useState} from 'react'

function Component(props){
	var [count, setCount] = useState(props.count)  ✅

  function inner() {
  	var [count, setCount] = useState(props.count)  ❌
  }
  
  if(count){
  	var [count, setCount] = useState(props.count)  ❌
  }
  
  return(
  	var [count, setCount] = useState(props.count) ❌
    <button onClick={()=>setCount(count+1)}></button>
  )
}

3. useInput

import {useState} from "react"

const useInput = (initialValue, validator) => {
  const [value, setValue] = useState(initialValue);
  const onChange = (event) => {
    const {
      target: { value }
    } = event;
    let willUpdate = true;
    if (typeof validator === "function") {
      willUpdate = validator(value);
    }
    if (willUpdate) {
      setValue(value);
    }
  };
  return { value, onChange };
};
const App = () => {
  const maxLen = (value) => value.length < 10;
  const name = useInput("Mr.", maxLen);
  return (
    <>
      <div className="APP">
        <h1>Hello</h1>
        <input placeholder="Name" {...name} />
      </div>
    </>
  );
};
  1. {value: "e", onChange: ƒ onChange()}

  2. {value: "e2", onChange: ƒ onChange()}

  3. {value: "e22", onChange: ƒ onChange()}

  4. onChange: 값이 바뀔 때 마다 onChange함수가 계속 작동하며 유효성검사를 거쳐 DOM을 refresh한다.

2. onChange함수는 event를 갖고 있다. 다른 function에서 이벤트를 처리할 수 있기 때문에 혁명적인 일이다. 이벤트를 분리된 파일, 다른 entity에 연결해서 처리할 수 있다.

4. useTabs

import React, { useState } from "react";

const content = [
  {
    tab: "Section1",
    content: "I am content1"
  },
  {
    tab: "Section2",
    content: "I am content2"
  }
];

const useTabs = (initialTab, allTabs) => {
  const [currentIndex, setCurrentIndex] = useState(initialTab);
  if (!allTabs || !Array.isArray(allTabs)) {
    return;
  }
  return {
    currentItem: allTabs[currentIndex],
    changeItem: setCurrentIndex
  };
};
const App = () => {
  const { currentItem, changeItem } = useTabs(0, content);
  return (
    <div className="App">
      {content.map((section, index) => (
        <button onClick={() => changeItem(index)}>{section.tab}</button>
      ))}
      <div>{currentItem.content}</div>
    </div>
  );
};

export default App;