◼ useState
Hook의 한 종류 중 하나로, 상태(state) 관리합니다. 상태 관리라 하면, 입력값, 변수값 등 값을 저장해야 하는 저장하는 컴포넌트별 메모리를 state라고 합니다.컴포넌트의 렌더링 사이에 데이터를 state에 유지하고 새로운 데이터로 컴포넌트를 다시 렌더링 하는 데 사용하는 Hook입니다.
주의사항
- 함수 안에서 set 함수를 호출한 후 값을 업데이트 하여도 state 변수에는 여전히 호출 전 값이 출력됩니다.
const [age, setAge] = useState(42); const [name, setName] = useState([]); function handleClick() { setAge(45); console.log(age); // 출력:42 setName([...name,'tome']); console.log(name); // 출력:[] } console.log(age); // 출력:45 console.log(name); // 출력:['tome']
- 사용자가 새로 값으로 변경 시 Objec.is()에 의해 현재 state와 동일하다고 판정되면, React는 컴포넌트와 그 자식들을 리렌더링 하지 않습니다.
const [count, setCount] = useState(0); // 기본: 0 // count 상태를 업데이트하는 함수 useEffect(() => { setTimeout(()=>{ setCount(prevCount => prevCount * 0); // 출력:0 그래서 랜더링 안됨 },2000) },[])
- React는 state를 일괄처리합니다. 모든 이벤트 핸들러가 실행되고 set 함수를 호출한 후에 화면을 업데이트합니다. 이렇게 하면 단일 이벤트 중에 여러 번 리렌더링 되는 것을 방지할 수 있습니다. 드물지만 DOM에 접근하기 위해 React가 화면을 더 일찍 업데이트하도록 강제해야 하는 경우, flushSync를 사용할 수 있습니다.
컴포넌트별 메모리
React 애플리케이션에서 각각의 컴포넌트가 자체적으로 상태를 저장하는 메모리 영역을 의미합니다.
즉, useState 하나를 가리키는 것이 아니라, 각각의 React 컴포넌트가 자체적으로 상태를 저장하는 메모리 영역을 의미합니다.
Object.is() 함수
Object.is()는 === 연산자보다 엄격한 동등성을 적용하며, 몇 가지 특수한 경우를 처리할 수 있습니다. 이것은 React에서 상태의 변경을 감지할 때 사용되는 방법 중 하나입니다. 상태를 변경할 때 React는 새로운 상태와 이전 상태를 Object.is()로 비교하여 변경 여부를 결정하고, 변경된 경우에만 화면을 다시 렌더링합니다.
flushSync() 함수
React에서 제공하는 동기적인(state 갱신 후) 렌더링을 수행하는 함수
주로 테스트 또는 성능 최적화에 사용됩니다. 특히 테스트에서는 특정 시점에서 컴포넌트가 렌더링을 완료했는지를 확인하고자 할 때 사용됩니다. 또한 flushSync()를 사용하여 사용자 상호작용에 의해 발생하는 상태 변경을 동기적으로 처리할 수 있습니다.
set 함수는 반환값이 없습니다.
그럼 상태 변수에 값을 어떻게 전달할까요? 정확하진 않지만.
set함수는 새로운 인자를 받게 되면, 상태를 스스로 업데이트하는 함수입니다. 내부적으로 상태를 업데이트하고, 이에 따라 컴포넌트를 다시 렌더링 시킵니다
구체적으로 말하면, React는 useState 훅을 사용하여 생성된 상태를 관리하는 데 필요한 내부 데이터 구조를 유지하고 있습니다. 이 내부 데이터 구조에는 상태 값이 저장되어 있으며, set 함수를 호출할 때마다 React는 해당 상태 값을 새로운 값으로 업데이트합니다. 그리고 이를 통해 컴포넌트의 상태가 변경되었음을 감지하고, 변경된 상태를 UI에 반영하여 다시 렌더링 합니다.
set 함수를 호출할 때마다 React는 내부적으로 상태 값을 업데이트하고, 변경된 상태를 자동으로 컴포넌트에 전달하여 렌더링을 트리거합니다. 이 과정에서 반환 값은 없으며, React가 내부적으로 관리하는 상태 값을 업데이트하는 것입니다. 개발자는 그저 set 함수를 호출하고, React가 나머지 작업을 처리하도록 합니다.
일반 변수로 실시간 업데이트가 안된다?!
function App() {
let aaaa = 1; // 기본 1
useEffect(() => { // 처음 딱 한번 무조건 실행
aaaa = 100; // 1을 100으로 바꿈
},[])
return (
<div className="App">
{aaaa} // 출력: 1
</div>
);
}
export default App;
React는 컴포넌트를 다시 렌더링 할 때 함수 내부에서 생성된 지역 변수의 변경사항을 고려하지 않습니다.
일반변수가 변경하더라도 React는 해당 컴포넌트를 다시 렌더링 하지 않기 때문에 UI에 변화가 반영되지 않습니다.
React 컴포넌트에서 함수 내부에 선언된 지역 변수의 값이 변경되더라도, React는 해당 변경사항을 감지하여 컴포넌트를 다시 렌더링하지 않습니다. React는 컴포넌트를 다시렌더링 할 때 상태(state)나 props의 변경 여부를 주로 고려합니다. 만약 렌더링을 발동시키기 위해서는 상태나 props를 변경해야 합니다.
실시간 랜더링이 되게 하려면?
const [value, setValue] = useState('');
첫번째 위치 : value => 상태변수
두번째 위치 : setValue => 상태 설정자 함수
상태변수:
useState를 사용하여 선언된 상태 변수를 state에 저장하여 컴포넌트의 렌더링 사이에 데이터를 변경된 데이터를 유지합니다. 이 변수는 컴포넌트가 다시 렌더링 될 때 이전 상태를 유지하고, 새로운 상태로 업데이트됩니다.
상태 설정자 함수:
선언된 상태 변수를 업데이트하는 데 사용되는 함수이며, 이 함수를 호출하여 상태를 변경하면 React는 해당 컴포넌트를 다시 렌더링 하여 새로운 데이터로 UI를 업데이트됩니다.
(상태변수 값이 바뀔 때마다 상태변수 사용한 영역만 재렌더링이 반복적으로 계속 일어납니다.)
◼ useState 문법
하나의 컴포넌트에 원하는 만큼 많은 유형의 state 변수를 가질 수 있습니다.
형태는 아래와 같이 생겼습니다.
// useState
const [value, setValue] = useState('');
const [null, setNull] = useState(null);
const [obj, setObj] = useState({
name: '이름',
age:24
});
첫번째 위치 : value => 상태변수/ 직접 값변경 안됩니다./ 읽기 전용으로 보면 된다
두번째 위치 : setValue => 상태 설정자 함수 / 상태변수의 값 변경을 하는 함수입니다
◼ useState 사용하기
import { useEffect,useState } from "react";
function App() {
const [age, setAge] = useState(42);
const [name, setName] = useState(['철수']);
return (
<div className="App">
// 상태변수를 원하는 곳에 {}안에 넣고 사용한다
<div>name:{name[0]}</div> // 출력: 철수
<div>age:{age}</div> // 출력: 42
</div>
);
}
export default App;
◼ useState 업데이트하기
import { useEffect,useState } from "react";
function App() {
const [age, setAge] = useState(42); // 초기값: 42
const [name, setName] = useState([]); // 초기값: [] 빈배열
useEffect(() => { // 랜더링시 처음 한번만 실행
setTimeout(()=>{
setAge(45); // age를 45로 숫자 변경
console.log(age); // 출력: 42 || + 추가 설명
setName([...name,'tome']) // 기존 배열에, 'tome'을 데이터를 추가 || + 추가 설명
console.log(name); // 출력: []
},2000)
},[])
return (
<div className="App">
<div>name:{name[0]}</div> // 출력: 'tome'
<div>age:{age}</div> // 출력: 45
</div>
);
}
export default App;
+ 추가 설명
useState 일반 값 변경이 안 되는 이유
set() 함수를 호출해도 실행 중인 코드의 state는 변경되지 않습니다
그 이유는 state가 스냅샷처럼 동작하기 때문입니다.
함수가 실행이 되고, 다음번 랜더링 때 바뀐 값이 적용됩니다.(이건 변수, 객체, 배열 다 포함)
+ 스냅숏처럼 동작
현재 상태가 이전 상태의 사진(스냅샷)을 보관한다는 의미입니다. 이전 상태의 사진을 보관해 두면서 상태가 변경될 때마다 새로운 사진을 찍어 보관합니다. 이렇게 하면 이전 상태를 변경하지 않고도 새로운 상태의 변경 부분만을 적용할 수 있습니다. 그래서 React는 변경된 부분만을 업데이트하고 관리할 수 있게 됩니다. 이 방식을 통해 React는 더 효율적으로 작동하고 성능을 최적화할 수 있습니다.
useState 객체, 배열 값 변경이 안 되는 이유
상태를 업데이트할 때 React가 해당 업데이트를 무시하는 경우가 있습니다.
React는 상태를 비교할 때 Object.is 메서드를 사용하며, 이를 통해 두 값이 완전히 동일한지를 확인합니다.
배열, 객체에 값을 추가하고 싶을 때, 새로운 배열을 만들어서 담아야 한다.
문제는 객체나 배열의 상태를 직접 변경하여 발생합니다. 예를 들어, 다음과 같이 기존 객체의 속성을 직접 변경한 후 상태를 업데이트하는 경우 React는 해당 업데이트를 무시합니다.
// 리액트에서 잘못된 방법 const state = { count: 0 }; // 기존의 state 객체 state.count = 1; // 객체를 직접 변이하여 업데이트 console.log(state); // 출력:{ count: 0 }
// 객체, 배열은 기존의 가지고 있는 값에 직접 수정하는 것이 아니라, 새로운 배열, 객체로 만들어서 전달해야 react가 무시하지 않고 랜더링을 합니다. const state = { count: 0 }; // 기존의 state 객체 const newState = { ...state, count: 1 }; // 새로운 객체를 생성하여 업데이트 console.log(newState); // 출력:{ count: 1 }
◼ 초기화
useState('초기값')을 통해 상태를 초기화할 때, 해당 초기값은 최초 렌더링 시에만 사용 처음에 한 번만 저장되고
다음 렌더링시 무시 됩니다.
이후에 set 함수를 사용하여 상태를 변경할 때 React는 해당 변경된 값을 기억하고 상태를 업데이트합니다. 이때 초기값은 다시 호출되지 않습니다.
따라서 초기값은 처음에만 호출되고 다시 호출되지 않으며, 상태가 변경될 때마다 React는 변경된 값을 기억하고 이를 다음 렌더링에 반영합니다.
함수 초기화
초기값을 설정할 때 함수를 초기값으로 사용할 수 있다. 렌더링 시에만 한 번 호출되며, 그 이후에는 무시됩니다. 이렇게 함으로써, 초기 상태를 설정하는 데 드는 작업 시간이나, 큰 데이터를 생성하는 작업을 최적화할 수 있습니다.
// 공식문서 예제 잘못된 예제
1.
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos()); // 안됨
// ...
createInitialTodos() 함수의 값을 초기값으로 가지고 있습니다.
이렇게 되면 함수를 실행한 값이 초기화 값 이기에, 실행이 두번 되고 무시 됩니다.
큰 배열을 생성하거나 값비싼 계산을 수행하는 경우 낭비가 될 수 있습니다.
function TodoList() {
// 함수자체를 를 초기화로 사용합니다
const [todos, setTodos] = useState(createInitialTodos);
// ...
◼ Key에 사용하기
key는 React에서 동적으로 생성된 목록의 요소를 고유하게 식별하는 데 사용됩니다. 이 key가 변경되면 React는 해당 요소를 새로운 것으로 간주하고 다시 렌더링 합니다.
key가 변경되면 리액트는 해당 컴포넌트를 다시 생성하고, 컴포넌트의 state를 "재설정"하는 것이 아니라, 새로운 컴포넌트 인스턴스를 생성하여 초기 상태로 설정하는 것입니다.
기존의 컴포넌트를 새로운 상태로 초기화해서 다시 사용하고 싶을 때 용이합니다
function App() {
const [version, setVersion] = useState(1);
const resetForm = () => {
setVersion(prevVersion => prevVersion + 1); // 키 변경
};
return (
<div>
<Form key={version} /> // 변경시 새로운 컴포넌트로 간주해서 초기상태로 돌림
<button onClick={resetForm}>Reset</button>
</div>
);
}
[참고]
https://react-ko.dev/reference/react/useState#resetting-state-with-a-key
https://react-ko.dev/learn/updating-objects-in-state
'[노트장] 적으며 정리해 보는 이론 > 리액트' 카테고리의 다른 글
[리액트] useRef (0) | 2024.03.28 |
---|---|
[리액트기초] Hook (0) | 2024.03.15 |
[리액트기초] style 입히기 (0) | 2024.03.15 |
[리액트기초] Props : 전달 (children도) (0) | 2024.03.15 |
[리액트기초] 리액트 실행법과 index.js 버전 에러 (0) | 2024.03.15 |