[React를 다루는 기술] 8장 - Hooks
😉 useState
- 첫 번째 인자로 현재 상태, 두 번째 인자로 상태를 바꾸는 함수
- 상태를 변화시킬 땐 항상 불변성 유지
import React, { useState } from "react";
export default function Counter() {
const [value, setValue] = useState(0);
return (
<div>
<h1>The number is {value}</h1>
<button
onClick={() => {
setValue(value + 1);
}}
>
+1
</button>
<button
onClick={() => {
setValue(value - 1);
}}
>
-1
</button>
</div>
);
}
😉 useEffect
- useEffect( function, deps? );
- function : useEffect가 수행 될 때 실행되는 함수
- deps [optional] : 배열 형태이며, 의존(dependency) 값을 의미한다.
deps[optinal] 값의 의미
- 마운트 될때, 업데이트 될때 - 값이 없을 경우
- useEffect(()⇒{})
- 화면이 렌더링 된 이후 수행이 되며, 리 렌더링이 발생하는 경우 다시 수행이 된다
- 마운트 될 때만 쓰임 - 빈 배열 인경우
- useEffect(()⇒{}, [])
- 화면이 렌더링 된 이후에만 수행이 된다.
- 특정값이 업데이트 될 때만 실행 - 배열 값이 존재하는 경우
- useEffect(()⇒{}, [값])
- 화면이 렌더링 된 이후에 수행이 되고, ‘값’이 변경되었을 경우 해당 메서드가 수행이 된다.
- deps에 특정 값을 넣게 된다면 컴포넌트가 처음 마운트 될 때, 지정한 값이 바뀔 때, 언마운트 될 때, 값이 바뀌기 직전에 모두호출이 된다.
💡clean-up 함수
-
Component의 unmount이전 / update직전에 어떠한 작업을 수행하고 싶다면 Clean-up함수를 반환해 주어야 한다.
- unmount 될 때 : useEffect(func, [])
- 특정값 update 직전 : useEffect(func, [특정값])
-
clean-up함수를 사용하게되면, 작동 순서는
- re-render -> 이전 effect clean-up -> effect
useEffect(() => {
console.log("일반 상황1 -> 마운트되어서 Info 컴포넌트가 보일 때");
console.log("일반 상황2 -> 정보가 업데이트 될 때");
return () => {
console.log("뒷정리 함수 상황1 : 언마운트 되어서 숨기려할 때");
console.log(
"뒷정리 함수 상황2 -> 업데이트 되기 직전에. 즉 input에 값 입력해도 업데이트 직전인 상황나옴. 리액트 -> 리액ㅌ"
);
console.log("상황 2는 2번째 배열에 값에 따라 달라짐");
};
}, [name]);
😉 useReducer
- 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 해주고 싶을 때
- 지금까지는 onClick 이벤트 같은 누르는 상황에만 값을 업데이트 했음.
- reducer(state, action). (현재 상태, 업데이트를 위해 필요한 정보를 담은 액션)
- action 값을 받아 새로운 상태로 변환 -> 불변성 유지
- 어떤 action인지 알려주는 type 필드 필요
- 컴포넌트 외부에 함수로 설정
- useReducer의 가장 큰 장점 : 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낸다.
function reducer(state, action) {
switch (action.type) {
case "INCREASE":
return { value: state.value + 1 };
case "DECREASE":
return { value: state.value - 1 };
default:
return state;
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, { value: 0 });
return (
<div>
<p>현재 카운터 값 : {state.value}</p>
<button onClick={() => dispatch({ type: "INCREASE" })}>+1</button>
<button onClick={() => dispatch({ type: "DECREASE" })}>-1</button>
</div>
);
};
😉 useMemo
- 컴포넌트 내부에서 발생하는 연산 최소화
- 실습 코드는 insert외에 input 내용 수정되어도 getAverage함수가 호출됨
- useMemo 사용하면 특정 값이 바뀌었을 때만 연산을 실행, 원하는 값이 바뀌지 않았다면 이전 연산 결과를 다시 사용
😉 useCallback
- 만들어 놨던 함수 재사용
- 렌더링 성능 최적화
- 그냥 함수 선언하면 렌더링 할 때마다 함수 다시 렌더링
- useCallback 사용하면 처음 렌더링 했을 때 만들어 놓은 함수 리렌더링 시 다시 안만들고 재사용
- 두 번째 인자로 업데이트 된 상태 적용하면, 그때만 리렌더링할 때 함수 다시 생성
import React, { useState, useMemo, useCallback } from "react";
const getAverage = (numbers) => {
console.log("calculating now");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
export default function Average() {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []);
const onInsert = useCallback(
(e) => {
const nextList = list.concat(parseInt(number, 10));
setList(nextList);
setNumber("");
},
[number, list]
);
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<h1>Average</h1>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>Enroll</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {avg}
</div>
</div>
);
}
😉 useRef
- ref 기능을 사용하고자 할 때 쓸 수 있다.
- 컴포넌트의 로컬 변수를 사용해야 할 때도 사용할 수 있다.
- useRef의 객체는 렌더링에 영향을 미치지 않는다.
const inputEl = useRef(null);
const onInsert = useCallback(e => {
...
inputEl.current.focus();
})
<input value={number} onChange={onChange} ref={inputEl} />
<button onClick={onInsert}>등록</button> //dom에 접근
const id = useRef(1);
const setId = (n) => {
id.current = n;
}
const printId = () => {
console.log(id.current) // 로컬 변수 (렌더링과 상관없이 바뀔 수 있는 값) 사용하기
}
😉 custom hooks
import { useContext } from "react";
import RouterContext from "../components/RouterContext";
const useRouter = () => {
const { setPath } = useContext(RouterContext);
const push = (path) => {
history.pushState({}, "", path);
dispatchEvent(new Event("popstate"));
setPath(path);
};
return { push };
};
export default useRouter;
Leave a comment