목표
- 컴포넌트의 상태와 관리에 대해 알아본다.
- 컴포넌트의 사이드 이펙트에 대해 알아본다.
<body>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const App = () => {
let keyword = "";
const onChange = (e) => {
keyword = e.target.value;
};
return (
<>
<input onChange={onChange} />
<button>search</button>
<p>searching... {keyword}</p>
</>
);
};
ReactDOM.render(<App />, rootElement);
</script>
</body>
<body>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const App = () => {
// let keyword;
const [keyword, setKeyword] = React.useState("");
const onChange = (e) => {
// keyword = e.target.value;
setKeyword(e.target.value);
};
return (
<>
<input onChange={onChange} />
<button>search</button>
<p>searching... {keyword}</p>
</>
);
};
ReactDOM.render(<App />, rootElement);
</script>
</body>
useState
- useState는 클래스 컴포넌트의 this.state의 기능을 한다.
- 일반적으로 일반 변수는 함수가 끝날 때 사라지지만 state 변수는 React에 의해 제거 되지 않는다.
- useState의 인자로 초기값을 넘겨준다. 숫자, 문자뿐만 아니라 객체까지 초기값으로 설정할 수 있다.
- useState가 반환하는 값은 state 변수와 해당 state 변수를 갱신 할 수 있는 함수 두 가지 쌍을 반환한다.
const keywordState = React.useState("initialValue");
console.log(keywordState[0]);
console.log(keywordState[1]);
// 1. 구조 분해 할당 X
const keywordState = React.useState("initialValue");
const keyword = keywordState[0];
const setKeyword = keywordState[1];
// 2. 구조 분해 할당 O
const [keyword, setKeyword] = React.useState("");
구조 분해 할당
- 자바스크립트 문법으로 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식이다.
- 자세한 내용은 Mozilla - 구조 분해 할당에서 확인할 수 있다.
🤔 의문
- 여러 개의 변수를 useState를 통해 관리하고 싶을 땐 어떻게 해야 할까?
useState는 state별로 독립적으로 관리하기 때문에 2개 이상의 변수를 저장한다면 useState를 그만큼 호출해주면 된다.
<body>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.getElementById("root");
const App = () => {
const [keyword, setKeyword] = React.useState("");
const [typing, setTyping] = React.useState(false);
const [result, setResult] = React.useState("");
const onChange = (e) => {
setKeyword(e.target.value);
setTyping(true);
};
const onClick = () => {
setTyping(false);
setResult(`find search result of ${keyword}`);
};
const onKeyPress = (event) => {
if (event.key === "Enter" && event.keyCode === 0) {
onClick();
}
};
return (
<>
<input onChange={onChange} onKeyPress={onKeyPress} />
<button onClick={onClick}>search</button>
<p>{typing ? `searching... ${keyword}` : result}</p>
</>
);
};
ReactDOM.render(<App />, rootElement);
</script>
</body>
localStorage
- localStorage를 사용하면 Document의 Storage 객체에 접근할 수 있다. 저장한 데이터는 브라우저 세션 간에 공유된다.
- 저장해야할 데이터가 별로 중요하지 않거나, 유실되도 무방할 데이터 즉, 서버 단에 데이터를 저장하는 것이 낭비라고 판단되면 사용하는 것이 좋다.
<script type="text/babel">
const rootElement = document.getElementById("root");
const App = () => {
const [keyword, setKeyword] = React.useState(
window.localStorage.getItem("keyword")
);
const [typing, setTyping] = React.useState(false);
const [result, setResult] = React.useState("");
const onChange = (e) => {
window.localStorage.setItem("keyword", e.target.value);
setKeyword(e.target.value);
setTyping(true);
};
// 생략
return (
<>
<input
onChange={onChange}
onKeyPress={onKeyPress}
value={keyword}
/>
<button onClick={onClick}>search</button>
<p>{typing ? `searching... ${keyword}` : result}</p>
</>
);
};
ReactDOM.render(<App />, rootElement);
</script>
// initialState
const [keyword, setKeyword] = React.useState(
window.localStorage.getItem("keyword")
);
💡 주의
- useState를 선언하는 부분에서 브라우저 내에 localStorage를 읽고 쓰는(I/O) 작업은 언제든 딜레이가 발생할 수 있기 때문에 초기값을 불러오지 못하는 문제가 발생할 수 있다.
그렇다면 딜레이 문제를 방지하려면 어떻게 해야할까?
// lazy initialState
const [keyword, setKeyword] = React.useState(() => {
return window.localStorage.getItem("keyword");
});
지연 초기화(lazy initialization)
- 지연 초기화는 오직 state가 처음 만들어 질 때만 실행된다. 이 후에 다시 리렌더링이 된다면, 이 함수의 실행은 무시된다.
- React 공식 문서에서는 초기 state가 고비용 계산의 결과라면 함수를 쓰는 것을 권장한다.
🤔 의문
- 그렇다면 모든 초기 값들을 지연 초기화(lazy initialization)으로 처리하면 되지 않을까?
🤔 의문
- 버튼을 눌렀을 때 말고 input에 값을 입력하여 keyword state 변수의 상태 값만 바뀔 때에만 localStorage에 값을 저장하려면 어떻게 해야할까?
React에서는 이러한 상황을 방지할 수 있도록 useEffect라는 훅(Hook)을 제공한다.
const App = () => {
console.log("render");
const [keyword, setKeyword] = React.useState(() => {
return window.localStorage.getItem("keyword");
});
const [typing, setTyping] = React.useState(false);
const [result, setResult] = React.useState("");
React.useEffect(() => {
console.log("effect");
window.localStorage.setItem("keyword", keyword);
}, [keyword]);
// 생략
useEffect
- userEffect를 사용해서 리액트에게 컴포넌트가 렌더링 이후에 어떤 일을 수행해야 하는 지를 말할 수 있다. 그러면 리액트는 우리가 넘긴 함수를 기억했다가(effect) DOM 업데이트를 수행한 이 후에 불러낸다.
- userEffect의 첫 번째 인자로는 어떤 일을 수행해야 하는지 말하는 함수를 넣어주고 두 번째 인자로는 사이드 이펙트를 일으키고 싶은 대상을 배열로 넣어주면 된다. 이 배열을 의존성 배열(dependency array)이라고 부른다.
의존성 배열
- 의존성 배열이 없다면? 모든 state 변수의 변화가 일어날 때마다 실행된다.
- 의존성 배열이 빈 값이면? 처음 렌더링 됐을 때 한 번만 실행된다.
- 의존성 배열에 값이 있으면? 그 state 변수들이 변할 때마다 실행된다.