5. Hook - 1(useState, useEffect)

맨날·2021년 5월 20일
0

Hook은 리액트 16.8 버전에 새롭게 추가된 기능입니다. Hook이 추가되면서 클래스형 컴포넌트를 작성하지 않고도 함수형 컴포넌트내에서 상태를 관리할 수 있게 되었고, 추가적인 기능들을 제공하게 되었습니다.

useState

useState는 가장많이 사용되는 Hook 중 하나일 것입니다. useState의 등장으로 인하여 함수형 컴포넌트 내에서도 상태를 관리할 수 있게 되었습니다.

useState의 파라미터로 초기값을 넣어줍니다. 이 초기값은 첫 번째 렌더링시 한번만 사용됩니다. useState을 호출하면 배열을 반환합니다. 첫번째 값으로는 상태 값, 두번째 값으로는 상태 값을 변경시킬 수 있는 setter를 반환합니다.

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

상태 값으로는 배열, 객체, 숫자 등 어떠한 것이든 가능합니다. 클래스형 컴포넌트처럼 하나의 객체에서 상태를 관리해도 되고, 여러 state 변수를 선언해서 사용해도 됩니다.

const [state, setState] = useState({
  name: '',
  nickname: ''
});
const [name, setName] = useState('');
const [nickname, setNickname] = useState('');

useEffect

useEffect는 컴포넌트가 렌더링 될때마다 특정 기능을 수행하도록 할때 사용합니다. useEffect를 호출할때 첫번째 파라미터로 함수를 넘겨주면 렌더링 될때 해당 함수가 호출이 됩니다.

useEffect는 DOM이 모두 그려진 후에 호출된다.

렌더링시마다 호출하기

두번째 파라미터를 넘겨주지 않고 첫번째 파라미터만 함수를 제공한다면, 렌더링이 될때마다 해당 함수가 호출이 됩니다.

아래의 코드를 작성 후 input의 값을 변경 시킬때마다 콘솔이 찍히는 걸 확인할 수 있습니다.

const Info = () => {
  const [name, setName] = useState('');
  const onChangeName = e => setName(e.target.value);
  
  useEffect(() => {
    console.log(name);
  });
  
  return (
    <div>
      <input value={name} onChange={onChangeName}/>
      <div>{name}</div>
    </div>
  )
};

export default Info;

컴포넌트가 마운트 될때 1번 호출하기

useEffect는 두번째 파라미터로 배열을 받습니다. 이때, 빈배열을 두번째 파라미터로 넘겨주면 컴포넌트가 마운트 될때 딱 1번만 호출되게 됩니다.

const Info = () => {
  useEffect(() => {
    console.log('마운트 될때 1번만 호출하기');
  }, []);
  
  return (
    <div>
      한번만 호출하기
    </div>
  )
}

export default Info;

특정 상태값이 변할때마다 호출하기

useEffect의 두번째 파라미터로 배열을 넘길때, 특정 상태값을 포함하여 넘겨주면 됩니다.

아래와 같이 배열에 name을 포함하여 넘겨주면, name 상태값이 바뀔 때만 호출되게 됩니다.

const Info = () => {
  const [name, setName] = useState('');
  const onChangeName = e => setName(e.target.value);
  
  useEffect(() => {
    console.log(name);
  }, [name]);
  
  return (
    <div>
      <input value={name} onChange={onChangeName}/>
      <div>{name}</div>
    </div>
  )
};

export default Info;

Clean-up(정리) 사용하기

컴포넌트가 언마운트 되거나, 컴포넌트가 리렌더링 되기 전에 코드를 실행해야할 경우가 존재합니다. 이럴 때 Clean-up을 사용할 수 있습니다.
Clean-up을 사용하는 방법은 useEffect 함수 내부에서 Clean-up 함수를 반환해주면 됩니다.

useEffect(() => {
  return () => {
    // Clean-up 코드가 들어갈 부분
  }
});

리렌더링 될때마다 호출하기

useEffect의 두번째 파라미터를 넘겨주지 않고, Clean-up 함수를 반환해주면 됩니다.

const Info = () => {
  const [name, setName] = useState('');
  const onChangeName = e => setName(e.target.value);
  
  useEffect(() => {
    return () => {
      console.log(name);
    }
  });
  
  return (
    <div>
      <input value={name} onChange={onChangeName}/>
      <div>{name}</div>
    </div>
  )
};

export default Info;

언마운트될 때 호출하기

컴포넌트가 언마운트될 때 한번만 호출하는 방법은 마운트 될때 1번 호출하는 방법과 유사합니다. 두번째 인자로 빈배열을 넘겨주고, Clean-up 함수를 반환해주면 됩니다.

const Info = () => {
  useEffect(() => {
    return () => {
      console.log('언마운트 될때 1번만 호출하기');
    }
  }, []);
  
  return (
    <div>
      한번만 호출하기
    </div>
  )
}

export default Info;

특정 상태값이 리렌더링 되기 전에 호출하기

useEffect의 두번째 파라미터로 배열을 넘길때, 특정 상태값을 포함하여 넘겨주고 Clean-up 함수를 반환해주면 됩니다.

const Info = () => {
  const [name, setName] = useState('');
  const onChangeName = e => setName(e.target.value);
  
  useEffect(() => {
    return () => {
      console.log(name);
    }
  }, [name]);
  
  return (
    <div>
      <input value={name} onChange={onChangeName}/>
      <div>{name}</div>
    </div>
  )
};

export default Info;

주의사항

useEffect 내부에서 사용되는 값을 배열에 포함시켜라

Clean-up시에 사용하는 상태값을 배열에 포함시키지 않는 경우 현재 값이 아닌 이전 렌더링 때의 값을 참고하게 됩니다.
테스트 결과 Clean-up함수가 아닌 곳에서는 정상적으로 현재값이 출력되는 것을 확인하였습니다.

아래와 같이 코드를 작성하고 먼저 name을 변경 후에 nickname을 변경하면, 현재 값이 아닌 이전 렌더링 값이 출력되는 것을 확인할 수 있습니다.

const Info = () => {
  const [name, setName] = useState('');
  const [nickname, setNickname] = useState('');
  const onChangeName = e => setName(e.target.value);
  const onChangeNickname = e => setNickname(e.target.value);
  
  useEffect(() => {
    return () => {
      console.log(name);
    }
  }, [nickname]);
  
  return (
    <div>
      <input value={name} onChange={onChangeName}/>
      <input value={nickname} onChange={setNickname}/>
      <div>{name}</div>
      <div>{nickname}</div>
    </div>
  )
};

export default Info;

아래의 코드를 작성 후, 동일하게 name을 변경 후에 nickname을 변경하면 name이 현재값으로 렌더링 되는 것을 확인할 수 있습니다.

const Info = () => {
  const [name, setName] = useState('');
  const [nickname, setNickname] = useState('');
  const onChangeName = e => setName(e.target.value);
  const onChangeNickname = e => setNickname(e.target.value);
  
  useEffect(() => {
    return () => {
      console.log(name);
    }
  }, [nickname, name]);
  
  return (
    <div>
      <input value={name} onChange={onChangeName}/>
      <input value={nickname} onChange={setNickname}/>
      <div>{name}</div>
      <div>{nickname}</div>
    </div>
  )
};

export default Info;

name을 변경하는 경우에는 이전 렌더링 결과가 출력됩니다.

0개의 댓글