자바스크립트였다면 const, let, var을 사용해서 변수를 선언했을텐데 React에서는 state라는 훅을 사용해 변수를 선언하고 저장한다.
useState는 배열로 리턴되고, 배열의 첫 번째는 설정값, 그리고 두 번째에는 값을 수정하는 setter함수로 구조가 배정되어있다.
//[리액트 공식문서]
const [state, setState] = useState(initialState);
사실 state에 대한 사용법은 너무 익숙히 다뤄봐서 포스팅을 하는게 맞나? 싶었지만, 공식문서를 읽어보니 알고있지 못했던 점이 많아 포스팅을 하는게 좋다고 생각했다.
●usestate 사용법 : 카운터
function App() {
const [count, setCount] = useState(0);
const plus = ()=>{
setCount(count + 1);
}
return (
<>
<h3>당신은 {count} 번을 눌렀습니다.</h3>
<button onClick={plus}>증가시키기</button>
</>
);
}
버튼을 누르면 1씩 올라가는 카운트 예제입니다. 굉장히 일반적이죵? 하지만, 버튼을 누를때마다 3씩 올라가게 만들고 싶다면 어떻게 해야할까요?
const plus = ()=>{
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}
plus 함수를 1씩 증가하는 setter함수를 3번을 넣었습니다. 하지만 이는 우리가 원하는대로 작동하지 않습니다.
- 왜냐하면 함수형 컴포넌트는 각 컴포넌트에 속한 count를 바라 '보기' 때문입니다.
- 따라서 저기 안에 있는 모든 count 은 0 이게 됩니다.
- setCount( 0 + 1 ) 를 3번 하고 있는 셈입니다.
function App() {
const [count, setCount] = useState(0);
const plus = ()=>{
setCount( c => c + 1);
setCount( c => c + 1);
setCount( c => c + 1);
}
return (
<>
<h3>당신은 {count} 번을 눌렀습니다.</h3>
<button onClick={plus}>증가시키기</button>
</>
);
}
따라서 위와 같이 수정하면 해결할 수 있습니다. c => c + 1이런 것을 updater(업데이터) 라고 합니다. 자신이 속해 있는 state가 어떤 것이든지 1씩 증가 시키겠다는 말인거죱.
이렇게 업데이터를 사용하는 경우가 늘 권장되는 것은 아니고, state변수에 직접적으로 접근하기에는 조금 꺼려질 때 사용되면 좋다. 예를들어, useEffect함수를 활용해서 렌더링 되면 딱 3번만 증가시키게 하고 싶은데, 이런 경우에는 어찌해야할까?
useEffect(()=>{
setCount(c => c+1);
setCount(c => c+1);
setCount(c => c+1);
}, [])
만약에, count + 1로 해야했다면, 의존성배열에 count를 넣어주는게 맞았을 것이다. 넣어주지 않았다면 의존성배열을 속이게 되는것23.
●usestate 사용법 : key를 통해 state rest하는 법
대부분 키를 쓰는 이유에 대해서 많이 접해보았을 것입니다. render key를 부착해야 dom에서 겹치지 않아지고 또 컴포넌트에서 바뀐 부분을 쉽게 찾아낼 수 있기 때문이기도 하지만, 다른 목적도 있습니다.
컴포넌트에 다른 키를 줌으로 써, state를 초기화 시킬 수 있습니다.
function App() {
const [version, setVersion] = useState(0);
function handleReset(){
setVersion( version + 1);
}
return(
<div>
<button onClick={handleReset}>Reset</button>
<Form key={version} />
</div>
)
};
function Form(){
const [name, setName] = useState('Taylor');
return (
<>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<p>Hello, {name}.</p>
</>
);
}
export default App;
App의 하위컴포넌트로 Form을 넣고 Reset버튼을 누를 때마다 version의 수가 올라가게 설정하였습니다.
이 경우, Reset버튼을 누를 때마다 state가 초기화됩니다.
왜그럴까요? 우리는 map 함수를 통해 여러 컴포넌트들을 만드는 작업을 한 경험이 있습니다. 그때마다 컴포넌트에 key값을 넣어주었죠. key값을 넣어줄 때마다 생성된 컴포넌트들은 각각 다른 key값을 가지고 있기 때문에 각각 다른 state를 가져야한다고 보는 것입니다.
따라서 다른 key값을 가진 컴포넌트들은 각각 다른 인스턴스로 취급됩니다. 위와 같은 상황은 새인스턴스가 생기게되고 자체 값이 초기화되는 것이라고 볼 수 있습니다. 이전 컴포넌트는 마운트가 해제되고 삭제됩니다.
진짜인지 확인해볼까요? Form 컴포넌트에 마운트와 언마운트 과정을 확인하기 위해 useEffectf를 사용하였습니다.
useEffect(()=>{
console.log('마운트됨ㅎㅎ')
return () =>{
console.log('언마운트됨 ㅠ')
}
}, [])
state가 변하는 것이 아니라, 진짜 마운트 -> 언마운트 -> 마운트 의 과정을 볼 수 있습니다.
●usestate 사용법 : 이전 state 추적하기
useEffect의 cleanUp함수를 쓰면, 이전 state값을 추적할 수 있었는데요, 그냥 state만으로 추적할 순 없을까요?
각각의 렌더링마다 분리된 값들 속에서 어떻게 이전 값을 가져올 수 있을까요?
App.js
function App() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
<CountLabel count={count} />
</>
);
}
App 컴포넌트에 버튼을 만들고, Increment를 누르면 증가하고, Decrement를 누르면 state가 감소하게 만들었습니다.
그리고 이 state를 자식의 컴포넌트에 넘겨줍니다. (CountLabel)
CountLabel.js
export default function CountLabel({ count }) {
const [prevCount, setPrevCount] = useState(count);
const [trend, setTrend] = useState(null);
if (prevCount !== count) {
setTrend(count > prevCount ? 'increasing' : 'decreasing');
setPrevCount(count);
}
return (
<>
<h1>{count}</h1>
{trend && <p>The count is {trend}</p>}
</>
);
}
- 보면 count로 props를 가져와 state 초기값을 설정합니다.
- 다음 props가 변하게 되고, 자식 컴포넌트는 props를 물려 받게 됩니다.
- 하지만 바뀐 props값으로 바로 state를 설정하지 않습니다.
- 구성요소가 다시 렌더링 될 때 이전 값을 비교하여 count의 추세를 비교할 수 있기 때문입니다.
- state는 그냥 바로 바뀌지않는다!!!!
●usestate 사용법 : 함수 저장법
// bad example
const [func, setFunc] = useState(solution);
// bad example
setFunc(solution)
만약에 solution이라는 함수가 있고, 그 값을 초기값으로 설정하고 싶거나, setFunc를 활용해서 state를 변경하고 싶다면 바로 함수명을 적어주면 안된다.
왜냐하면 soltuion 함수자체를 저장하는게 아니라, soltuion함수가 반환하는 return 값을 저장하게 되기 때문이다.
함수로 저장하고 싶다면
// correct example
const [func, setFunc] = useState(()=> soltuion );
// correct example
setFunc(()=> solution)