state
컴포넌트 안에서 관리되는 유동적인 데이터이다.
useState
[value, setterFunc] = useState()
🤔 리액트 컴포넌트가 다시 렌더되는 조건
함수 컴포넌트가 렌더된다. = 함수 컴포넌트가 다시 실행되었다.
1.prop
이 업데이트 된 경우
2.state
가 업데이트 된 경우
3. 부모 컴포넌트가 다시 렌더된 경우 (1, 2번과 같은 이유 등으로 리렌더링 된 경우) 👉 이 경우 자식 컴포넌트도 다시 렌더링된다.
🤔
update
를 판단할 때 주의할 점(deep vs shallow)
- Primitive Type인 경우 값까지 비교
- Reference Type(Object, Array 등)인 경우 참조까지만 비교 👉 안쪽에 있는 값까지 비교하진 않는다!
다음 코드에서 리액트는left
의 값을 바꿔도 렌더를 다시 해야 한다고 생각하지 않는다. (레퍼런스 타입이므로)left[0]
의 값을 바꾼다고 해도left
자체는 그대로이므로 렌더하지 않는 것이다.
right
값을 먼저 입력하고,left
를 입력하면 결과가 나오지 않는다.
그런데left
를 먼저 입력하고,right
를 입력하면 결과가 나오는데, 이때는right
(프리미티브 타입) 값이 변경되면 렌더되므로 값이 나오게 되는 것이다.
(setLeft(left)
를 하더라도 주소값이 변경되지 않았으므로 렌더는 되지 않는다!)
따라서value
의 값을 직접적으로 변경하는 것이 아니라,setter
함수를 통해 값을 변경해야 한다. (useState
로 값을 바꾼다.(❌),setter
함수로 값을 바꾼다.(⭕))
또한, 레퍼런스의 타입의 경우 주의해야 한다!
state
업데이트의 비동기성
비동기성때문에 setNum(num+1)
첫 번째 줄에서 num+1
이 처리되기 전에 두 번째 줄로 넘어갈 수도 있다. 우리가 기대하는 것은 <1+1 → 2+1 → 3+1, 결과값: 4>이지만, 이것이 아닌 <1+1 → 1+1 → 1+1, 결과값: 2>이 된다.
그래서! setNum
이 호출될 당시의 num
값을 콜백함수 형태로 넘겨준다. setNum(prev => prev + 1)
(setState는 비동기로 작동하므로 순서가 필요할 경우 콜백함수 형태로 작동해야 하는 것.)
이때, 한 줄씩 실행되는 것이 아니라 같은 스테이트라도 매우 짧은 시간 안에 업데이트가 일어날 시 리액트는 한번에 묶어서 처리한다.(= 렌더가 한 번만 일어난다.) → 배치 업데이트(Batch update)
setNum
자체가 동기적인 것이 아니라 함수들끼리 동기적으로 작동하는 것이다!
state
, side effect
를 다루기 위해 사용하는 함수이다.useState
, useEffect
, useMemo
...import React, {useState} from 'react'
export default function Calculator() {
const [result, setResult] = useState(0);
const [num1, setNum1] = useState(0);
const [num2, setNum2] = useState(0);
return (
<>
<input type="number" value={num1} onChange={(event) => setNum1(parseInt(event.target.value))} />
+
<input type="number" value={num2} onChange={(event) => setNum2(parseInt(event.target.value))}/>
=
<input type="number" disabled value={result} />
<button type="button" onClick={() => setResult(num1+num2)}>계산</button>
</>
)
}
🤔 re-render는 언제 될까?
처음 실행했을 때 1번!
input에 값이 입력될 때마다!
'계산' 버튼을 클릭했을 때 1번!
🤔
readonly
vsdisabled
둘 다 읽기만 가능하고 값을 변경할 수 없다.
하지만,disabled
는form
으로 값을 보낼 때 값이 전송되지 않는다.
다음 코드를 변경해서 버튼을 클릭할 때마다 'hellojinbye'가 출력되도록 하자.
const names = [{
name: 'hello',
}, {
name: 'jin',
}, {
name: 'bye',
}];
const App = () => {
const [result, setResult] = React.useState<string[]>([]);
return (
<>
{ /*result*/ ['hello', 'jin', 'bye']}
<button type='button' onClick={() => {}} />
</>
);
}
import React, {useState} from 'react';
const names = [{
name: 'hello',
}, {
name: 'jin',
}, {
name: 'bye',
}];
const App = () => {
const [result, setResult] = useState([]);
return (
<>
{result}
<button type='button' onClick={() => {
// setResult([...result, ...names.map(({name})=>name)]);
setResult(
[...result, ...names.map((element) => {return element.name})]
);
}} >버튼</button>
</>
);
}
export default App;
🤔 setState를 이용해 배열을 state로 관리
step1. 배열을 하나 새로 만든다. >> 새로운 메모리 주소를 가지고 있는 배열.
step2. 배열에 대해서 작업을 함.let temp = [...state]
step3.setState[...아까 만든 배열]