형태: const [state, setState] = useState(initialState);
불변성이란
일반 사용법과 함수형 업데이트 방식
1) 일반 사용적인 방식
setState(number + 1);
2) 함수형 방식
setState(() => {});
3) 두 방식의 차이점
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 리액트는 성능을 위해 setState()를 단일 업데이트(batch update)로
한꺼번에 처리할 수 있다.
불필요한 리-렌더링을 방지(렌더링 최적화)하기 위해
현재 버튼을 누르면 1씩 증가하게 되는 걸 볼 수 있다. */}
<div>{number}</div>
<button
onClick={() => {
setNumber(number + 1); // 첫번째 줄
setNumber(number + 1); // 두번쨰 줄
setNumber(number + 1); // 세번째 줄
}}
>
버튼
</button>
</div>
);
}
export default App;
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 현재 상태(prev)를 가져와 함수 형태로 실행하여 각각 처리한다.
버튼을 누르면 3씩 증가된다. */}
<div>{number}</div>
<button
onClick={() => {
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
}}
>
버튼
</button>
</div>
);
}
export default App;
리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 때 사용한다.
useEffect
는 화면에 컴포넌트가 mount 또는 unmount 됐을 때 실행하고자 하는 함수를 제어하게 해주는 훅이다.useEffect
에서 함수를 1번만 실행시키고자 할때는 의존성 배열을 빈 배열로 둔다.어떤 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶다면? 또는 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면? useEffect를 사용
import React, { useEffect, useState } from 'react'
function UseEffect() {
const [value, setValue] = useState("");
useEffect(() => {
console.log(`hello useEffect: ${value}`);
//return문을 함수로 선언하면 해당 컴포넌트가 종료될 시에 실행된다.
return () => {
console.log("컴포넌트가 종료될 시 실행된다.!!")
}
})
return (
<>
<input value={value} onChange={(event) => setValue(event.target.value)}></input>
</>
)
}
// 해석
// 1. input에 값을 입력
// 2. value, 즉 state가 변경
// 3. state가 변경될 당시 ==> 컴포넌트가 리-랜더링됨
// 4. 리랜더링 ==> useEffect 실행
// 즉, 값을 입력할 때마다 useEffect가 실행되는 걸 볼 수 있다.
// 여기서 특정 값이 변경될 때만 useEffect를 실행하게 하려면
// 의존성 배열(dependency array)을 넣으면 된다.
:특정 조건의 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행하게 한다.
//위의 코드에서 useEffect() 부분만 가져왔다.
useEffect(() => {
console.log(`hello useEffect: ${value}`);
return () => {
console.log("컴포넌트가 종료될 시 실행된다.!!")
}
},[]) // []이 부분이 의존성 배열이다.
//의존성 배열이 빈 배열인 경우
// useEffect는 처음에 딱 한번만 실행되고 그 이후로는 어떤일이 일어나도 실행되지 않는다.
//의존성 배열에 값이 있을 경우
// 배열의 값이 state 될때마다 실행된다.
// 현재 배열에 [value] 값을 넣는다면 input 태그에 값을 입력될 때마다 작동 되는 걸 볼 수 있다.
state와 비슷한 역할이지만 서로 간의 차이점이 존재
- useState는 변화가 일어나면 리-렌더링이 일어나 내부 변수들이 초기화된다.
- useRef는 한 번 저장된 값은 렌더링을 일으키지 않으며 컴포넌트가 렌더링 되더라도
ref 값은 unmount 전까지 값을 유지한다.
- useState는 리-렌더링이 꼭 필요한 값을 다룰 때 쓰면 된다.
ex) 버튼 클릭시- useRef는 리-렌더링을 발생시키지 않는 값을 저장할 때 사용한다.
ex) 렌더링 되자마자 특정 input이 focusing 돼야 한다면 useRef를 사용한다.
import React, { useEffect, useState } from 'react'
import { useRef } from 'react';
function UseRef() {
const [id, setId] = useState("");
const [password, setPassword] = useState("");
const idRef = useRef("");
const pwRef = useRef("");
//idRef.current.focus();
//함수 컴포넌트의 바디 외부에 있으므로 React의 렌더링 주기와 무관하게 동기적으로 실행됩니다.
//이것은 React의 규칙에 위배되며, 해당 DOM 요소가 마운트되기 전에 focus() 메서드가 호출되므로 오류가 발생
// 화면이 렌더링 될 때, 어떤 작업을 하고싶다: useEffect
// 현재 id를 useState로 선언하여 id의 값이 변경될 때마다 랜더링이 되는 걸 볼 수 있다.
// 그래서 아래의 useEffect 부분은 id가 변경될 때마다 실행되는 걸 볼 수 있다.
// 방법 1
useEffect(() => {
idRef.current.focus();
if(id.length > 9){
pwRef.current.focus();
}
console.log("useEffect 실행")
},[id]) //id가 변경될 때만 작동한다.
//방법 2
// const handleIdChangeEffect = (e) => {
// const value = e.target.value;
// setId(value);
// if (value.length > 9) {
// pwRef.current.focus();
// }
// console.log("useEffect 실행")
// }
//batch update 처리로 인해 값이 바로 변경되지 않는다.
//setId 이후 값이 바로 변경되지 않기 때문에 id.length가 정상작동 되지 않는(밀리는 현상)" 볼 수 있다.
//이러한 이유로 랜더링 될 경우에만 수행되는 useEffect를 사용하는 것이다.
// const handleIdChangeEffect = (e) => {
// setId(e.target.value);
// console.log(id);
// if (id.length > 9) {
// pwRef.current.focus();
// }
// }
return (
<>
<form>
<div>아이디: <input type='text' ref={idRef} value={id} onChange={(e) => setId(e.target.value)} autoComplete='username'/> </div>
{/* <div>아이디: <input type='text' ref={idRef} value={id} onChange={handleIdChangeEffect} autoComplete='username'/> </div> */}
<div>비밀번호: <input type='password' ref={pwRef} value={password} onChange= {(e) => setPassword(e.target.value)} autoComplete='current-password'/></div>
</form>
</>
)
}
export default UseRef
: props drilling 방지 (전역 데이터를 관리)
<구성>
createContext
: context 생성Consumer
: context 변화 감지Provider
: context 전달(to 하위 컴포넌트)import { createContext } from "react";
//context 생성
export const FamilyContext = createContext("");
import React from 'react'
import Father from './Father'
import { FamilyContext } from './FamilyContext';
function GrandFather() {
const houseName = "스파르타";
const pocketMoney = 10000;
return (
// Provider에서 제공한 value(houseName, pocketMoney)가 달라진다면
// useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 된다.
<FamilyContext.Provider value={{houseName,pocketMoney,}}>
<Father/>
</FamilyContext.Provider>
)
}
export default GrandFather
import React from 'react'
import Child from './Child'
function Father() {
return (
<Child></Child>
)
}
export default Father
import React, { useContext } from 'react'
import { FamilyContext } from './FamilyContext';
const styles = {
color: "red",
fontWeight: "900",
};
function Child() {
// Context를 사용하여 받아온 값
const data = useContext(FamilyContext);
console.log(data);
return (
<div>
나는 이 집안의 막내에요.!!<br/>
할아버지가 우리집 이름은 <span style={styles}>{data.houseName}</span>라고 하셨습니다.<br/>
게다가 용돈도 <span style={styles}>{data.pocketMoney}</span>원만큼 주셨어요!
</div>
)
}
export default Child
주의사항
Provider에서 제공한 value가 달라진다면 useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 됩니다. 따라서 value 부분을 항상 신경써야한다.