728x90
반응형
SMALL
리액트 기초부터 공부하는 와중에 친구의 추천으로 리액트 기본기 질문 영상을 보았는데 "예상 결괏값"을 맞춘게 하나도 없어서 필자는 react의 기본기에 대해 부족한 것 같아 영상에 말한 것을 정리해보았다.
이 글을 보고 있는 당신도 영상을 한번 보는 것을 권장한다.
1번
- 결과 값은 어떻게 되는지?
function App() {
console.log("init");
const [state, setState] = useState(0);
console.log("state:", state);
useEffect(() => {
console.log("effect");
setState(1);
}, []);
return <div className="App"></div>;
}
[정답]
function App() {
console.log("init");
const [state, setState] = useState(0);
console.log("state:", state);
useEffect(() => {
console.log("effect");
setState(1);
}, []);
return <div className="App"></div>;
}
// 결과
// init
// state: 0
// effect
// init
// state: 1
2번
- 결과 값은 어떻게 되는지?
function App() {
const [state, setState] = useState(0);
console.log("state:", state);
useEffect(() => {
setState(1);
}, [state]);
return <div className="App"></div>;
}
[정답]
function App() {
const [state, setState] = useState(0);
console.log("state:", state);
useEffect(() => {
setState(1);
}, [state]);
return <div className="App"></div>;
}
// 결과
// state: 0
// state: 1
// state: 1
- dependency array에 state를 넣으면 3개의 결괏값이 나온다.
- 함수형 컴포넌트에서 **useState**를 사용할 때, 상태(state)를 변경하는 함수인 **setState()**를 호출하면 컴포넌트가 다시 렌더링된다.
첫 번째 호출:
- 컴포넌트가 처음 렌더링될 때, **useState(0)**으로 초기 상태를 설정한다.
- state 값은 0이 되고, **console.log("state:", state);**가 실행되어 "state: 0"이 출력된다.
두 번째 호출:
- **useEffect**는 컴포넌트가 렌더링될 때와 상태가 변경될 때마다 실행됩니다.
- **useEffect**의 의존성 배열에 **[state]**를 지정했으므로, state 값이 변경될 때마다 **useEffect**가 실행됩니다.
- **setState(1)**을 호출하여 상태를 1로 변경합니다.
- **console.log("state:", state);**가 다시 실행되는데, 이때 state 값은 이미 1로 변경된 상태입니다. 따라서 "state: 1"이 다시 출력됩니다.
즉, setState(1)로 인해 state가 1이 되고, state상태값이 변경되면서 위에서 코드가 실행되어 state: 1 출력함. 그리고 dependency array의 state값을 지정했으므로, state값이 변경되면 setState(1)가 또 실행된다.
3번
- 결과 값은 어떻게 되는지?
function App() {
const [state, setState] = useState(0);
console.log("state:", state);
useEffect(() => {
setState(1);
return () => { // 클린업 함수
console.log(state);
};
}, [state]);
return <div className="App"></div>;
}
[정답]
function App() {
const [state, setState] = useState(0);
console.log("state:", state);
useEffect(() => {
setState(1);
return () => { // 클린업 함수
console.log(state);
};
}, [state]);
return <div className="App"></div>;
}
// state: 0
// state: 1
// 0
// state: 1
- 첫 번째 렌더링에서 **state**의 초기값은 0이므로, **useEffect**가 실행되면 **setState(1)**이 호출되어 state 값이 1로 변경되지만, 이 때 클린업 함수는 여전히 첫 번째 렌더링 때의 state 값인 0을 참조하게 되어 0을 출력한다.
- 이는 JavaScript의 클로저 특성 때문이다. **useEffect**의 클린업 함수는 state 값이 변경되더라도 클린업 함수가 최초에 선언된 시점의 state 값을 계속 참조하게 됩니다.
4번
- 결과 값은 어떻게 되는지?
function App() {
const [state, setState] = useState(0);
useEffect(() => {
setState(1);
console.log(state);
setState(2);
console.log(state);
setState(3);
console.log(state);
}, [state]);
return <div className="App"></div>;
}
[정답]
function App() {
const [state, setState] = useState(0);
useEffect(() => {
setState(1);
console.log(state); // 0
setState(2);
console.log(state); // 0
setState(3); // 최종적으로 3이 나옴
console.log(state); // 0
}, [state]);
return <div className="App"></div>;
}
// 결괏값
// 0
// 0
// 0
// 3
// 3
// 3
실행순서
- useEffect 내부에서 console.log(state) 를 호출하면 setState 호출 이후에도 state 값이 업데이트되지 않기 때문에 항상 초기값인 0이 출력된다.
- 그리고 useEffect의 두 번째 인자로 state를 전달하기 때문에 state 값이 변경될 때마다 useEffect내부의 코드가 실행된다.
- 마지막에 있는 setState(3)을 호출하면 state 값이 3으로 변경되고, 이로 인해 useEffect가 다시 실행되어 console.log(state)가 3을 출력하여 총 3이 반복되어 출력된다.
5번
function App() {
const [state, setState] = useState(0);
console.log(state);
useEffect(() => {
setState(state + 1);
setState(state + 1);
setState(state + 1);
}, []);
return <div className="App"></div>;
}
[정답]
function App() {
const [state, setState] = useState(0);
console.log(state);
useEffect(() => {
setState(state + 1); // 0+1
setState(state + 1); // 0+1
setState(state + 1); // 0+1
}, []);
return <div className="App"></div>;
}
// 결괏값
// 0
// 1
- useEffect의 두 번째 인자로 빈 배열([])을 전달했기 때문에 useEffect 내부의 코드는 컴포넌트가 처음 렌더링될 때 단 한 번만 실행된다. 그래서 state 값은 1로 변경된 후 더 이상 변경되지 않아 1이 출력된다.
function App() {
const [state, setState] = useState(0);
console.log(state);
useEffect(() => {
setState(1); // 1
}, []);
return <div className="App"></div>;
}
// 결괏값
// 0
// 1
- 엄밀히 말하면 이런식으로 표현된다.
6번
- 이전 state가 반영되는 문제
function App() {
const [state, setState] = useState(0);
console.log(state);
useEffect(() => {
setState((prev) => prev + 1);
setState((prev) => prev + 1);
setState((prev) => prev + 1);
}, []);
return <div className="App"></div>;
}
[정답]
function App() {
const [state, setState] = useState(0);
console.log(state);
useEffect(() => {
setState((prev) => prev + 1);
setState((prev) => prev + 1);
setState((prev) => prev + 1);
}, []);
return <div className="App"></div>;
}
// 결괏값
// 0
// 3
- useEffect 내부에서 **setState((prev) => prev + 1)**을 세 번 호출하고 있다. 이 때 setState 함수에 함수를 인자로 전달하면, React는 이전 state 값을 인자로 해당 함수를 호출하여 새 state 값을 계산한다.
- 따라서 세 번의 setState 호출로 인해 state 값은 **0 + 1 + 1 + 1 = 3**으로 변경된다.
7번
function App() {
const [state, setState] = useState(0);
const [value, setValue] = useState(0);
console.log(value);
useEffect(() => {
setState(3);
setValue(state);
}, []);
return <div className="App"></div>;
}
[정답]
function App() {
const [state, setState] = useState(0);
const [value, setValue] = useState(0);
console.log(value);
useEffect(() => {
setState(3);
setValue(state);
}, []);
return <div className="App"></div>;
}
// 결괏값
// 0
// 0
- 이 코드에서는 useEffect 내부에서 **setState(3)**을 호출한 후에 **setValue(state)**를 호출하고 있다. 이 때 state 값은 **useEffect**가 호출될 때의 값, 즉 **0**을 가지고 있다. 따라서 **setValue(state)**는 **setValue(0)**이 되어 value 값은 **0**으로 설정된다.
- useEffect의 두 번째 인자로 빈 배열([])을 전달했기 때문에 useEffect 내부의 코드는 컴포넌트가 처음 렌더링될 때 단 한 번만 실행됩니다. 따라서 value 값은 **0**으로 변경된 후 더 이상 변경되지 않는다.
8번
- 밑 코드의 결과값처럼 콘솔에 두번 출력되는 이유는?
import { useEffect, useState } from "react";
function App() {
useEffect(() => {
console.log("effect");
}, []);
return <div className="App"></div>;
}
export default App;
// 결괏값
// effect
// effect
[정답]
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<StrictMode>
<App />
</StrictMode>
);
- <StrictMode>의 여부에 따라 useEffect 안에서 코드이 호출 횟수가 달라진다.
※ 출력되는 이유는 검증용으로 실행되는 것이다.
참고
728x90
반응형
LIST