React hook

React hook 규칙
1. 반드시 컴포넌트 최상단에서 선언해야 한다.
2. ‘함수형’ 컴포넌트 내부에서만 호출해야 한다.
useState
가장 기본적인 훅이라고 할 수 있다.
리액트에서 컴포넌트를 만들면서 ‘상태’를 관리해야 하는 경우가 많다.
그냥 변수를 만들면 어떤 함수나 이벤트에 의해 값이 변경되었을 때, 화면에 바로 반영되지 않는다.
대신 useState를 사용하면 변수의 상태를 관리하고 상태가 변경되었을 때마다 재렌더링한다.
const [ state, state 변경 함수 ] = useState(초깃값)
setState = (state + 1)
이런 식으로 사용한다. 만든 state를 변경하고 싶으면 state를 직접 건드리는 것이 아니라 배열의 두번째 값인 state변경 함수를 사용해 변경해야 한다.
const [ name , setName ] = useState(“박초은“)
const [ age , setAge ] = useState(18)
const [ birth , setBirth ] = useState(0303)
하나의 컴포넌트에 여러 개의 useState를 사용하는 것이 나쁜 것은 아니지만 관련된 정보라면 하나의 객체로 표현할 수도 있다.
const [ human , setHuman ] = useState({
name : “박초은“,
age : 18,
birth : 0303,
});
이렇게 하면 name, age, birth를 각각 관리하는 것이 아니라 human에 접근해서 한번만 변경해주면 된다.
하지만 객체로 state를 이용하면 useState가 새로운 객체로 바꾸기 때문에 기존 객체를 spread문법으로 가져온 다음 변경해야 한다.
왜냐하면 js에서 배열, 객체 등은 변수에 값을 저장하는 것이 아니라 값이 저장된 메모리의 주소를 저장하기 때문에 값이 같아도 저장된 공간이 다르면 다른 변수라고 인식하기 때문이다.
setHuman({
…human,
name : ”김철수“,
age : 35,
birth : 1225,
});
자바스크립트 es6 에서는 비구조화 할당이라는 것이 있는데, 이를 활용해 state 와 변경 함수를 배열에 쓰지 않고 변수처럼 사용할 수 있다.
const numberState = useState(0);
const number = numberState[0];
const setNumber = numberState[1];
const onIncrease = () => {
setNumberr(preNum => preNum + 1);
}
useRef
함수형 컴포넌트를 사용할 때 useState를 쓰면 state가 변경될 때마다 컴포넌트가 재렌더링 되면서 컴포넌트 내부 함수나 변수가 전부 초기화된다. 이를 방지하기 위해 useRef를 사용하는 게 성능 향상에 더 좋을 때가 있다.
즉, 컴포넌트가 계속 렌더링 되어도 컴포넌트가 언마운트되기 전까지 값을 그대로 유지한다.
그래서 useRef는 어떤 값의 변화를 감지해야 하지만 변화했을 때 렌더링을 발생시키면 안되는 경우 사용하면 좋다.
const number = useRef(1)
변수명과 초기값을 설정하면 결과값으로 { current : 초기값 } 이라는 객체가 반환된다.
따라서 변수의 값을 변경하기 위해서는 number.current.value에 접근해야 한다.
current 속성에 접근해 값을 변경해도 state를 변경했을 때처럼 컴포넌트가 재렌더링 되지 않는다.
cosnt inputRef = useRef(null)
useEffect(() => {
inputRef.current.focus()
}, []);
<input ref={inputRef} />
useRef를 이용해서 요소에 접근해 더 편리한 기능을 구현할 수도 있다.
input 요소에 inputRef로 접근한 다음 useEffect로 컴포넌트가 마운트되었을 때 input 요소를 클릭하지 않아도 focus가 되어있도록 할 수 있다.
const [ count, setCount ] = useState(0)
const number = useRef(0)
function increaseState() {
setCount(count + 1);
console.log(“state는“, count);
}
function increaseRef() {
++number.current;
console.log(“ref는“, number);
}
return (
<div>
<p>{count}</p>
<button onClick={increaseState}>State 증가</button>
<p>{number}</p>
<button onClick={increaseRef}>Ref 증가</button>
</div>
);
위와 같은 컴포넌트를 만들어보면 state 와 ref 의 차이를 알기 쉽다.
버튼으로 state를 증가시키면 state가 변했기 때문에 컴포넌트를 다시 렌더링한다.
Ref를 증가시키면 콘솔에 찍히는 값은 변하지만 화면에 나타나는 숫자는 그대로이다. 이 상태에서 state를 증가시켜보면 재렌더링이 되면서 ref의 값도 화면에 반영되는데, 0으로 초기화되는 것이 아니라 마지막에 변화시킨 값이 그대로 나타난다. 그리고 state를 계속 증가시켜도 ref의 값이 변하지 않는다.
useEffect
리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 실행할 수 있도록 하는 Hook 이다.
리액트에서 컴포넌트는 생성(mounting) -> 업데이트(updating) -> 제거(unmounting) 의 생애주기를 갖는다.
useEffect는 컴포넌트가 mount 됐을 때, unmount 됐을 때, update 됐을 때 실행할 수 있다.
useEffect(() => {
//실행할 기능
}, []);
useEffect 는 첫번째 인자로 콜백 함수를 받고 두번째 인자로 Dependency Array 라고 하는 의존성 배열을 받는다.
의존성 배열에 들어가는 값이 변경될 때만 실행되게 설정하거나, 빈 배열을 넘기면 컴포넌트가 마운트 될 때 한 번만 실행한다. 의존성 배열이 없다면 컴포넌트가 재렌더링될 때마다 실행한다.
useEffect(() => {
// 실행할 코드
return () => {
// 실행할코드
}
}, []);
useEffect 내부에서 타이머 등 setInteval을 사용하거나 구독 처리 같은 작업을 실행한다면 더이상 타이머를 사용하지 않거나 구독을 해지할 때를 위한 코드를 작성해야 한다. cleanup function 이라고 하는데, useEffect 안에 return 뒤에 작성해 준다. 의존성 배열이 빈 배열이면 컴포넌트가 언마운트 될 때만 실행되고, 값이 있다면 그 값이 업데이트되기 직전에 실행한다.
useEffect 를 사용할 때 주의점은 useEffect 내에서 어떤 state를 변경한다면 useState는 state가 변경될 때마다 재렌더링이 되고, 컴포넌트가 새로 마운트 되었으므로 useEffect 가 실행된다. 그러면 useEffect 안에서 또 state가 변경되어 다시 렌더링이 되고 … 무한루프가 발생하기 때문에 꼭 의존성 배열을 넣어줘야 한다.
다음은 최적화 훅 써야지