elements의 집합
상태 값을 관리해주는 Hook
위의 코드에서 setState를 두고 요소가 변경될 때마다 다시 render 해주어야 하는 불편함이 있었는데, useState() 를 사용해서 쉽게 상태를 변경해준다.
useState는 [state 변수와 해당 변수를 갱신할 수 있는 함수] 이 쌍, 배열을 반환한다.
const [keyword, setKeyword] = React.useState(
window.localStorage.getItem("keyword")
);
useState 사용시, 이런 식으로 윈도우의 localstorage 에서 처음 값, 데이터를 가져올 때 render 하는데에 시간이 오래 걸릴 수 있다.
useState의 인자로는 무엇이든 들어올 수 있고, useState 인자로 함수로 초기화 시킴으로써 render 이후에 함수 내부의 코드가 실행될 수 있도록 해준다. (lazy initialize) 의도적으로 함수를 실행시킴으로써 delay를 걸어주는 것이다.
side effect = 의도하지 않은 효과, 부수 효과
어떤 값이 변경될 때마다만 작동하는 함수를 독립적으로 쓰고 싶을 때 사용할 수 있다.
우리의 경우 초기값에서 keyword가 바뀔 때만 localstorage에 저장하는 함수가 실행되면 좋겠다. (부수적으로) 버튼을 누를 때도 localstorage가 실행되고... 하여튼 비효율적이다.
그래서 독립적인 공간에 원하는 코드를 넣고 실행할 수 있도록 한다.
React.useEffect(function, dependency array)
function : 부수적으로 일어났으면 하는 것.
어떤 값이 변경될 때 실행할 함수
dependency array : 의도한 것.
위에서 '어떤 값'. 배열로 여러 값을 지정해 줄 수도 있다. 이 값이 변화될 때 부수효과를 낸다.
아예 값을 주지 않을 경우 모든 상황에서 함수를 실행한다.
빈 배열로 둘 경우, 부수효과는 처음 한 번만 동작한다.
useState, useEffect 같은 hook을 직접 커스텀해서 만들어보자.
React.useEffect(() => {
window.localStorage.setItem("keyword", keyword);
}, [keyword]);
React.useEffect(() => {
window.localStorage.setItem("result", result);
}, [result]);
keyword, result 각각 바뀔 때 독립적으로 저장하기 위해서는 이런 식으로 코드를 작성해야한다. 하지만 하는 일은 비슷한데, 코드의 중복이 우리를 불편하게 한다.
-> 커스텀 훅!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
</head>
<body>
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
// 우리가 원하는 이름으로 state를 만들고, 로컬스토리지에 넣어주는 함수
// value는 빈 문자열로 초기화
function useLocalStorage(itemName, value = "") {
const [state, setState] = React.useState(() => {
// itemName 넣어주거나, 그 값이 없으면(처음이면) value 값 넣어주기
// keyword, result는 처음에는 아무 값도 없기 때문에
// 인자에서 설정했던 "" 빈 문자열이 들어오고
// typing은 state 만들어줄 때 false로 값을 넘겨주기 때문에
// default 값이 그것으로 설정된다.
return window.localStorage.getItem(itemName) || value;
});
// 값이 변경될 때 로컬스토리지에 넣어주기
React.useEffect(() => {
window.localStorage.setItem(itemName, state);
}, [state]);
return [state, setState];
}
const App = () => {
const [keyword, setKeyword] = useLocalStorage("keyword");
const [result, setResult] = useLocalStorage("result");
const [typing, setTyping] = useLocalStorage("typing", false);
function handleChange(event) {
setKeyword(event.target.value);
setTyping(true);
}
function handleClick() {
setTyping(false);
setResult(`We find ${keyword}`);
}
return (
<>
<input onChange={handleChange} value={keyword} />
<button onClick={handleClick}> search </button>
<p> {typing ? `Looking for ${keyword}` : result} </p>
</>
);
};
ReactDOM.render(<App />, rootElement);
</script>
</body>
</html>
처음
버튼 누르기 전
버튼 누른 후
function handleClick() {
setShow((prev) => !prev);
}
setState 할 때 이전 값인 prev가 담겨있다.
Hook들의 호출 타이밍
!! 변화 발생시
-> render start
-> useState
-> render end
-> Child render start
-> Child useState
-> Child render end
-> Child useEffect (작성 순서대로)
-> useEffect (작성 순서대로)
상태 값이 바뀌어 rerender 되면,
부모의 useEffect는 자식의 useEffect가 다 끝난 이후에야 동작한다.
useEffect
render가 끝나고 동작
자식이 있다면 자식의 useEffect 이후에 부모 useEffect
update 시
어떤 값이 변경 될 때 useEffect clean up이 먼저 일어난 이후에 useEffect 동작 useEffect clean up -> uesEffect
부모의 useEffect부터 clean up
한번이라도 useEffect 등록됐으면 clean up, 처음에는 clean up X
dependency array
전달 받은 갑스이 변화가 있는 경우에만. 작성 순서대로.