useState

import { useState } from "react";
export const State = () => {
const heavyWork = () => {
console.log("무거운 작업");
return ["길동이", "김방구"];
};
const [name, setName] = useState(["길동이", "김방구"]);
const [input, setInput] = useState("");
const handleInput = (pre) => {
setName((pre) => {
console.log(pre);
return [input, ...name];
});
};
const handleInputChange = (e) => {
setInput(e.target.value);
};
return (
<div>
<input
type="text"
className="w-50 bg-red-400"
value={input}
onChange={handleInputChange}
/>
<button onClick={handleInput} className="w-10 bg-active">
버튼
</button>
<p>{name}</p>
</div>
);
};

하지만 이렇게 , state가 업데이트 될때마다 컴포넌트가 렌더링 된다.
만약 state가 무거운 작업을 해야한다면, 컴포넌트가 계속해서 렌더링이 됨 !!
state초기값에 들어있는 무거운 작업을 하는 함수가 계속해서 호출 될 것임 ==>성능저하
import { useState } from "react";
const heavyWork = () => {
console.log("무거운 작업");
return ["길동이", "김방구"]; //초기값
};
export const State = () => {
const [name, setName] = useState(heavyWork());
const [input, setInput] = useState("");
const handleInput = (pre) => {
setName((pre) => {
console.log(pre);
return [input, ...name];
});
};
const handleInputChange = (e) => {
setInput(e.target.value);
};
return (
<div>
<input
type="text"
className="w-50 bg-red-400"
value={input}
onChange={handleInputChange}
/>
<button onClick={handleInput} className="w-10 bg-active">
버튼
</button>
<p>{name}</p>
</div>
);
};

이렇게 업로드를 할때마다 무거운 작업이 계속 불려짐
내가 원하는 것 : heavyWork가 처음 렌더링 될때만 불려지기
초기값을 넣을 인자에 callback으로 넘김
const [name, setName] = useState(() => heavyWork());

한번만 불림~
즉 초기값을 가져올때 , 무거운 작업을 해야한다면, 값을 가져오는게 아니라 , callback함수를 통해 처리하자
useEffect
생명주기 메서드들은 클래스형 컴포넌트에서만 사용할 수 있었다. 하지만 훅의 등장으로 useEffect를 사용할 수 있게 되면서 함수형 컴포넌트에서도 "생명주기 시점에 따른 사이드 이펙트"를 실행할 수 있게 되었다.
useEffect(()=>{},deps)
// 첫번째 인자로 callback함수를 받고,
// 두번째 인자로 deps를 받는다.
// deps: 배열 형태이며 deps에 넣어진 특정 값은 컴포넌트가 마운트 될 때, 지정한 값이 update될 때 실행된다.


- componentDidMount
- 컴포넌트를 다 만들고, 렌더링을 마친 후에 실행
- 해당 메서드는 컴포넌트가 DOM에 마운트(=최초렌더링) 된 이후 호출된다. 이 시점에서 컴포넌트가 이미 화면에 나타나고,
- componentDidUpdate
- 리렌더링을 완료한 후 실행, render()가 업데이트 될 때마다
- 업데이트는 prop이나 state의 변경으로 인해 발생한다
- 이 메서드는 첫 번째 렌더링에는 호출 안됨
- componentWillUnmount
- 컴포넌트를 DOM에서 제거할 때 실행
- 컴포넌트를 마운트 해제하기 직전에 호출
생명주기에 따른useEffect확인하기

컴포넌트 마운트(mount)
- 최초 렌더링
- useEffect 내부 코드로직이 실행된다
- 클래스 생명주기 메서드componentDidMount와 비슷한 역할을 하게 된다
컴포넌트 업데이트(updating)
- 렌더링 후 업데이트
- 컴포넌트가 마운트 된 이후 상태나 props가 변경되면 컴포넌트가 다시 리렌더링 된다.
- 이때 useEffect에 전달된 콜백함수 또한 다시 실행된다
- 클래스 생명주기 메서드 componentDidUpdate와 비슷한 역할을 하게 된다
컴포넌트 마운트 해제(unmount)
- 컴포넌트가 화면에서 제거될 때, useEffect에 전달된 함수의 반환값인 clean-up 함수가 호출
- 클래스 생명주기 메서드 componentWillUnmount와 비슷한 역할을 하게 된다
import { useEffect, useState } from "react";
export const Effect = () => {
const [count, setCount] = useState<number>(1);
const [name, setName] = useState<string>("");
const handleUp = () => {
setCount((pre) => pre + 1);
};
const handleInputChange = (e) => {
setName(e.target.value);
};
useEffect(() => console.log("렌더링될때마다"));
return (
<div>
<div className="flex gap-2 items-center">
<button
onClick={handleUp}
className=" border-red-300 border bg-white p-2"
>
update
</button>
<p>{count}</p>
</div>
<input
type="text"
className="bg-amber-200"
value={name}
onChange={handleInputChange}
/>
<span>{name}</span>
</div>
);
};

현재 deps를 추가하기 전 이기 때문에, 글자를 입력하면 reder되는것을 확인
useEffect(
() => console.log("렌더링될때마다,업데이트된 count ", count),
[count]
);

하지만 문제!!
Q. 의존성 배열에 객체가 존재한다면?
의존성 배열의 모든 값들의 종속성(참조 값)을 비교하고, 다르다면 첫번째 파라미터로 받은 함수를 동작시킨다.
새로운 렌더링마다 동일한 객체라도 새롭게 생성(메모리 주소 변경)되면 React는 이를 변경되었다고 판단하여 useEffect를 다시 실행시킨다. 이를 해결하려면 객체 내부의 특정 값만 추출하거나, useMemo를 사용하여 객체 참조가 유지되도록 하거나, 의존성 배열에 객체 대신 객체 안의 원시 타입 속성들을 넣는 것이 좋다.
값 vs 참조
- 값
- number, string, boolean, null, undefined, bigint, symbol
- 변수 안에 값 자체가 들어있는 경우
let a = 10;
let b = 10;
console.log(a === b); // true (값 비교)
- 참조
- object, array, function, Date, Map 등
- 변수 안에는 “객체가 저장된 메모리 위치(참조)”가 들어있다.
const obj1 = { x: 1 };
const obj2 = { x: 1 };
const obj3 = obj1;
console.log(obj1 === obj2); // false (서로 다른 객체(주소값))
console.log(obj1 === obj3); // true (같은 객체(주소값)를 가리킴)
obj1과 obj2는 모양은 같지만 서로 다른 객체임.
obj1과 obj3는 정확히 같은 객체를 공유
deps를 처리하면서 , 문제가 될 때
1. 객체의 속성 일부가 바뀌는 경우
function ExampleComponent() {
const [state, setState] = useState({ a: 1, b: 1 });
useEffect(() => {
console.log('state가 변경되었을 때마다 이 console.log가 실행됩니다.');
}, [state]); // state 객체의 참조가 바뀔 때마다 useEffect가 호출된다.
const handleClick = () => {
setState(prevState => ({ ...prevState, a: prevState.a + 1 }));
// a 값만 바뀌어도 새로운 객체가 생성되어 state 참조가 바뀐다.
};
return (
<div>
<button onClick={handleClick}>Click me</button>
</div>
);
}
이럴땐 밑에처럼 원시값으로 넣어줌(너무 당연하지만 그냥 언급..)
useEffect(() => {
console.log('state가 변경되었을 때마다 이 console.log가 실행됩니다.');
}, [state.b]);
2. 객체의 생성 연산이 복잡할 때 - useMemo나 useCallback을 통해 처리
const [obj, setObj] = useState({ count: 0, flag: false });
const memoizedObj = useMemo(
() => generateObject({ count: 0, flag: false }),
[count, flag],
);
useEffect(() => {
console.log('memoizedObj가 변경되었을 때마다 이 console.log가 실행됩니다.');
}, [memoizedObj]);
'React' 카테고리의 다른 글
| [React] lazy loading을 사용한 초기 렌더링 최적화 (0) | 2026.03.17 |
|---|---|
| Suspense 와 Error Boundary를 사용한 로딩과 에러 처리 (0) | 2026.01.08 |
| react hook -useRef (0) | 2026.01.06 |