지금까지는 값이 변하지 않는 정적인 리액트 컴포넌트를 만들었습니다. 지금부터는 사용자의 행위나 시간 변동에 따라 값이 변하는 동적인 리액트 컴포넌트를 만들 차례입니다. 이를 위해서는 리액트의 핵심 기능 중 하나인 State 를 알아야 합니다. 이번 절에서는 State 를 이용해 동적인 컴포넌트를 만드는 방법을 살펴보겠습니다.
State 이해하기
State 는 상태라는 뜻입니다. 상태는 어떤 사물의 형편이나 모양을 일컫는 말로 일상 생활에서도 흔히 사용합니다.
상태는 전구와 스위치에 빗대어 상객하면 쉽게 이해할 수 있습니다. 스위치를 끄면 전구에 불이 들어오지 않는데, 이를 '소등 상태' 라고 할 수 있습니다. 반대로 스위치를 켜면 전구에 불이 들어오며 이를 '점등 상태' 라고 할 수 있습니다.
전구의 상태 변화는 다음과 같이 정리할 수 있습니다.
- 전구의 상태는 소등과 점등으로 나눌 수 있습니다.
- 소등 상태일 때 스위치를 켜면 '점등' 으로 상태 변화가 일어납니다.
- 점등 상태일 때 스위치를 켜면 '소등' 으로 상태 변화가 일어납니다.
용어를 상태가 아닌 State 로 변경하면 다음과 같습니다.
- 전구 State 는 off(소등), on(점등) 둘 중 하나의 값을 갖습니다.
- 전구 State 의 값이 off 일 때, 스위치를 켜면 값이 on 으로 바뀝니다.
- 전구 State 의 값이 on 일 때, 스위치를 켜면 값이 off 으로 바뀝니다.
전구의 상태와 리액트 컴포넌트의 State 는 매우 유사합니다. 전구의 상태가 상태 변화에 따라 점등 또는 소등으로 변하는 것처럼 리액트 컴포넌트 또한 State 값에 따라 다른 결과를 렌더링합니다.
State 의 기본 사용법
직접 State 를 만드는 실습을 하면서 리액트의 State 가 어떤 개념인지 자세히 알아보겠습니다.
useState 로 State 를 생성하기
리액트에서는 함수 useState 로 State 를 생성합니다.
useState 의 문법은 다음과 같습니다.
useState의 용법
const [light, setLight] = useState('off');
State 변수 set 함수 생성자(초깃값)
useState 를 호출하면 2개의 요소가 담긴 배열을 반환합니다. 이때 배열의 첫 번째 요소 'light' 는 현재 상태의 값을 저장하고 있는 변수입니다. 이 변수를 'State 변수' 라고 부릅니다.
다음으로 두 번째 요소인 setLight 는 State 변수의 값을 변경하는 즉 상태를 업데이트하는 함수입니다. 이 함수를 'set 함수' 라고 부릅니다. useState 를 호출할 때 인수로 값을 전달하면 이 값이 State 의 초깃값이 됩니다. 위 코드에서는 'off' 를 전달했으므로 State 변수 light 의 초깃값이 off 가 됩니다.
Body 컴포넌트에서 숫자를 카운트할 수 있는 State 변수 count 를 생성하겠습니다. Body.js 를 다음과 같이 수정합니다.
src/component/Body.js
import { useState } from "react"; ①
function Body() {
const [count, setCount] = useState(0); ②
return (
<div>
<h2>{count}</h2>
</div>
);
}
export default Body;
① useState 는 리액트가 제공하는 State 를 만드는 함수입니다. State 를 만들기 위해 useState 를 react 라이브러리에서 불러옵니다.
② 함수 useState 인수로 State 의 초깃값을 전달합니다. 코드에서는 초깃값으로 0 을 전달합니다. 그 결과 State 변수 count 와 set 함수 setCount 를 반환합니다.
저장하고 페이지에서 결과를 확인합니다.
State 변수 count 를 만들 때, 함수 useState 에서 인수로 0 을 전달했기 때문에 페이지에서는 0 을 렌더링합니다.
set 함수로 State 값 변경하기
이번에는 set 함수를 호출해 State 값을 변경하겠습니다. 컴포넌트에서 버튼을 하나 만들고, 버튼을 클릭할 때마다 State(count) 값을 1 씩 늘리겠습니다. Body 컴포넌트를 다음과 같이 수정합니다.
import { useState } from "react";
function Body() {
const [count, setCount] = useState(0);
const onIncrease = () => { ①
setCount(count + 1);
};
return (
<div>
<h2>{count}</h2>
<button onClick={onIncrease}>+</button>
</div>
);
}
export default Body;
① 버튼의 이벤트 핸들러 onIncrease 에서는 set 함수인 setCount 를 호출합니다. 인수로 count 에 1 더한 값을 전달합니다.
페이지에서 < + > 버튼을 클릭하면 onIncrease 이벤트 핸들러가 실행됩니다. 함수 onIncrease 는 setCount 를 호출하고, 인수로 현재의 count 값에 1 더한 값을 전달합니다. 그 겨로가 State(count) 값은 1 증가합니다.
페이지에서 < + > 버튼을 클릭해 컴포넌트를 어떻게 렌더링하는지 확인합니다.
< + > 버튼을 클릭할 때마다 숫자가 1 씩 늘어난는 것을 확인할 수 있습니다.
이렇듯 set 함수를 호출해 State 값을 변경하면, 변경값을 페이지에 반영하기 위해 컴포넌트를 다시 렌더링합니다. 리액트에서는 이것을 '컴포넌트의 업데이트' 라고 표현합니다.
컴포넌트가 페이지에 렌더링하는 값은 컴포넌트 함수의 반환값입니다. 따라서 컴포넌트를 다시 렌더링한다고 함은 컴포넌트 함수를 다시 호출한다는 의미와 같습니다. 컴포넌트 함수를 다시 호출한다는 게 어떤 의미인지 직접 확인해 보겠습니다.
다음과 같이 Body 를 호출할 때마다 콘솔에 문자열 Update! 를 출력하도록 Body 컴포넌트를 수정합니다.
src/component/Body.js
import { useState } from "react";
function Body() {
console.log("Update!"); ①
const [count, setCount] = useState(0);
const onIncrease = () => {
setCount(count + 1);
};
return (
<div>
<h2>{count}</h2>
<button onClick={onIncrease}>+</button>
</div>
);
}
export default Body;
① Body 컴포넌트를 호출할 때마다 콘솔에 문자열 Update! 를 출력합니다.
저장한 다음, 페이지에서 < + > 버튼을 정확히 8 번 클릭한 다음 콘솔을 확인합니다.
처음 나온 Update! 는 컴포넌트를 처음 렌더링할 때 출력된 것입니다. 나머지 8번의 Update! 는 < + > 버튼을 클릭할 때마다 Body 컴포넌트를 다시 호출하기 때문에 출력되었습니다.
컴포넌트는 자신이 관리하는 State 값이 변하면 다시 호출됩니다. 그리고 변경된 State 값을 페이지에 렌더링합니다. State 값이 벼해 컴포넌트를 다시 렌더링하는 것을 '리렌더' 또는 '리렌더링' 이라고 합니다. 리액트 컴포넌트는 자신이 관리하는 State 값이 변하면 자동으로 리렌더됩니다.
State 로 사용자 입력 관리하기
웹 사이트에서는 다양한 입력 폼을 제공하는데, 사용자는 이 입력 폼을 이용해 텍스트, 숫자, 날짜 등의 정보를 입력합니다. HTML 에서 입력 폼을 만드는 태그로는
- 다양한 형식의 정보를 입력할 수 있는 <Input> 태그,
- 여러 옵션에서 하나를 선택하도록 드롭다운(DropDown) 목록을 보여주는 <Select> 태그,
- 여러 줄의 텍스트를 입력할 수 있는 <Textarea> 태그 등이 있습니다.
입력 폼은 로그인, 회원가입, 게시판, 댓글 등이 필요한 페이지에서 자주 활용되는 웹 개발의 필수 요소입니다. 리액트에선 State 를 이용하면 다양한 입력 폼에서 제공되는 사용자 정보를 효과적으로 처리할 수 있습니다.
<input> 태그로 텍스트 입력하기
처음 다룰 입력 폼은 텍스트, 전화번호, 날짜, 체크박스 등 여러 형식의 정보를 입력할 수 있는 input 태그가 만드는 폼입니다. input 태그로 텍스트를 입력하는 폼을 하나 만들고, 사용자가 텍스트를 입력할 때마다 콘솔에 출력하는 이벤트 핸들러를 구현하겠습니다.
Body 컴포넌트를 다음과 같이 수정합니다.
src/component/Body.js
import { useState } from "react";
function Body() {
const handleOnChange = (e) => { ①
console.log(e.target.value);
};
return (
<div>
<input onChange={handleOnChange} /> ②
</div>
);
}
export default Body;
① 입력 폼에서 이벤트 핸들러로 사용할 함수 handleOnChange 를 만듭니다. 이 함수는 이벤트 객체를 매개변수로 저장해 사용자가 폼에 입력한 값(e.target.value) 을 콘솔에 출력합니다.
② input 태그로 텍스트를 입력할 폼을 만들고, 이 폼을 onChange 이벤트 핸들러로 handleOnChange 를 설정합니다. onChange 이벤트는 사용자가 입력 폼에서 텍스트를 입력하면 바로 발생합니다.
※ <input> 태그로 만들 수 있는 입력 폼은 매우 다양합니다. <input> 태그의 type 속성에서 text 를 지정하면 텍스트 폼, date 를 지정하면 날짜 형식의 폼, tel 을 지정하면 전화번호 형식의 폼을 만드비다. 이외에도 라디오 버튼이나 체크박스 등도 <input> 태그를 이용해 만들 수 있습니다. type 속성에서 아무것도 지정하지 않으면 기본 입력 폼인 텍스트 폼을 만듭니다.
코드를 저장하고 페이지의 입력 폼에서 임의의 텍스트를 입력합니다. 그리고 개발자 도구의 콘솔에서 어떤 변화가 일어나는지 확인합니다.
텍스트를 입력하는 즉시 콘솔에서도 입력한 텍스트를 출력합니다.
이 상태로도 텍스트 입력 폼을 이용해 사용자에게 입력을 받을 수 있습니다. 그러나 지금은 사용자가 입력한 텍스트가 리액트 컴포넌트가 관리하는 State 에 저장되어 있지는 않습니다. 따라서 만약 버튼을 클릭했을 때 사용자가 입력한 텍스트를 콘솔에 출력하는 등의 동작을 수행하게 하려면 돔 API 를 이용하는 등 번거로운 작업이 별도로 요구됩니다.
따라서 State 를 하나 만들고 사용자가 폼에서 입력할 때마다 텍스트를 State 값으로 저장하겠습니다.
Body 컴포넌트를 다음과 같이 수정합니다.
src/component/Body.js
import { useState } from "react";
function Body() {
const [text, setText] = useState(""); ①
const handleOnChange = (e) => {
setText(e.target.value); ②
};
return (
<div>
<input value={text} onChange={handleOnChange} /> ③
<div>{text}</div> ④
</div>
);
}
export default Body;
① 빈 문자열을 초깃값으로 하는 State 변수 text 를 생성합니다.
② 폼에 입력한 텍스트를 변경할 때마다 set 함수를 호출해 text 값을 현재 입력한 텍스트로 변경합니다.
③ <input> 태그의 value 속성에 변수 text 를 설정합니다.
④ 변수 text 의 값을 페이지에 렌더링합니다.
정리하면 입력 폼에서 사용자가 텍스트를 입력하면 onChange 이벤트가 발생해 이벤트 핸들러 handleOnChange 를 호출합니다. handleOnChange 는 내부에서 set 함수를 호출하는데, 인수로 현재 사용자가 입력한 텍스트를 전달합니다. 그 결과 사용자가 폼에서 입력한 값은 text 에 저장되면서 State 값을 업데이트합니다.
State 값이 변경되면 컴포넌트는 자동으로 리렌더됩니다. 따라서 페이지에서는 현재의 State 값을 다시 렌더링합니다. 저장하고 페이지의 입력폼에서 임의의 텍스트를 입력해 어떻게 변하는지 확인합니다.
폼에서 임의의 텍스트를 입력하면, 다음 줄에 입력한 텍스트를 그대로 렌더링합니다.
<input> 태그로 날짜 입력하기
<input> 태그에서 type 속성을 "date" 로 설정하면 날짜 형식의 데이터를 입력할 수 있습니다. 이번에는 State 를 이용해 날짜 형식의 데이터를 입력 정보로 받아보겠습니다.
Body 컴포넌트를 다음과 같이 수정합니다.
src/component/Body.js
import { useState } from "react";
function Body() {
const [date, setDate] = useState("");
const handleOnChange = (e) => {
console.log("변경된 값: ", e.target.value);
setDate(e.target.value);
};
return (
<div>
<input type="date" value={date} onChange={handleOnChange} />
</div>
);
}
export default Body;
<input> 태그에서 type 을 date 로 설정하면 onChange 이벤트가 발생했을 때 이벤트 객체의 e.target.value 에는 문자열로 이루어진 yyyy-mm-dd 형식의 날짜가 저장됩니다.
따라서 날짜 형식을 지정하는 별도의 처리 없이도 텍스트 폼에서 입력할 때처럼 State 값을 날짜 형식으로 저장할 수 있습니다. 저장한 다음, 변경된 페이지를 확인합니다. 날짜 형식으로 입력할 수 있는 폼이 만들어집니다. 날짜 입력 폼 오른쪽에 나타나는 캘린더 아이콘을 클릭해 날짜를 바꾸면서 State 값이 콘솔에서 어떻게 나타나는지 직접 확인합니다.
입력 폼에서 날짜를 변경하면 콘솔에서도 변경된 날짜가 바로 출력됩니다.
드롭다운 상자로 여러 옵션 중에 하나 선택하기
<select> 태그는 <option> 과 함께 사용합니다. 이 태그를 사용하면 드롭다운(DropDown) 메뉴로 여러 목록을 나열해 보여 주는 입력 폼이 만들어집니다.
이 폼 목록에서 하나를 선택하면 해당 항목을 입력할 수 있습니다. 드롭다운 입력 폼은 쇼핑몰 사이트에서 여러 옵션을 선택할 때 자주 활용됩니다. 드롭다운 입력 폼에서 입력한 값을 State 로 어떻게 처리하는지 알아보겠습니다.
Body 컴포넌트를 다음과 같이 수정합니다.
src/component/Body.js
import { useState } from "react";
function Body() {
const [option, setOption] = useState("");
const handleOnChange = (e) => {
console.log("변경된 값: ", e.target.value);
setOption(e.target.value);
};
return (
<div>
<select value={option} onChange={handleOnChange}>
<option key={"1번"}>1번</option>
<option key={"2번"}>2번</option>
<option key={"3번"}>3번</option>
</select>
</div>
);
}
export default Body;
드롭다운 입력 폼에서 사용자가 옵션을 변경하면 onChange 이벤트가 발생합니다. 이때 이벤트 핸들러에 제공되는 이벤트 객체 e.target.value 에는 현재 사용자가 선택한 옵션의 key 속성이 저장됩니다. 만약 사용자가 3번 옵션을 선택하면 해당 옵션의 key 속성인 3번이 e.target.value 에 저장됩니다. 따라서 이 값으로 현재 State 에 저장된 값을 변경합니다.
저장한 다음, 페이지에서 드롭다운 입력 폼을 클릭해 3번으로 옵션을 변경합니다. 그리고 콘솔에서 어떤 변화가 있는지 확인합니다.
드롭다운 입력 폼에서 3번을 선택한 결과 콘솔에서는 변경된 값이 3번임을 표시합니다.
글상자로 여러 줄의 텍스트 입력하기
<textarea> 태그는 사용자가 여러 줄의 텍스트를 입력할 때 사용하는 폼을 만듭니다. 이 폼은 웹 페이지에서 사용자가 자기소개와 같이 여러 줄의 내용을 입력할 때, 주로 활용됩니다. 이 폼을 편의상 '글 상자' 라고 하겠습니다. 이번에는 글 상자에 입력한 내용을 State 로 어떻게 처리하는지 살펴보겠습니다.
Body 컴포넌트를 다음과 같이 수정합니다.
src/component/Body.js
import { useState } from "react";
function Body() {
const [text, setText] = useState("");
const handleOnChange = (e) => {
console.log("변경된 값 : ", e.target.value);
setText(e.target.value);
};
return (
<div>
<textarea value={text} onChange={handleOnChange} />
</div>
);
}
export default Body;
<textarea> 는 <input> 태그의 입력 폼과 동일한 형태로 텍스트를 처리합니다. 사용자가 텍스트를 입력하면 onChange 이벤트가 발생합니다. 이때 이벤트 핸들러는 이벤트 객체의 e.target.value 에 저장된 값을 인수로 전달해 State 값을 변경합니다.
저장하고 글 상자에서 안녈리액트라고 입력한 다음 콘솔에서 확인합니다.
글자를 입력할 때마다 콘솔에서는 입력한 텍스트가 바로 표시됩니다.
여러 개의 사용자 입력 관리하기
지금까지 리액트의 State 를 이용해 컴포넌트에서 사용자의 입력을 처리하는 방법을 알아보았습니다. 그런데 회원 가입을 유도하는 페이지에는 사용자의 입력 폼이 하나가 아니라 작게는 3개, 많게는 10개까지 되는 곳도 있습니다.
이번에는 여러 개의 사용자 입력을 State 로 관리하는 방법을 살펴보겠습니다. 이름, 성별, 출생 연도, 자기소개 등을 한 번에 입력할 수 있도록 Body 컴포넌트를 다음과 같이 수정하겠습니다.
src/component/Body.js
import { useState } from "react";
function Body() {
const [name, setName] = useState("");
const [gender, setGender] = useState("");
const [birth, setBirth] = useState("");
const [bio, setBio] = useState("");
const onChangeName = (e) => {
setName(e.target.value);
};
const onChangeGender = (e) => {
setGender(e.target.value);
};
const onChangeBirth = (e) => {
setBirth(e.target.value);
};
const onChangeBio = (e) => {
setBio(e.target.value);
};
return (
<div>
<div>
<input value={name} onChange={onChangeName} placeholder="이름" /> ①
</div>
<div>
<select value={gender} onChange={onChangeGender}> ②
<option key={""}></option>
<option key={"남성"}>남성</option>
<option key={"여성"}>여성</option>
</select>
</div>
<div>
<input type="date" value={birth} onChange={onChangeBirth} /> ③
</div>
<div>
<textarea value={bio} onChange={onChangeBio} /> ④
</div>
</div>
);
}
export default Body;
① <input> 태그의 입력 폼에서 이름을 받고, State(name) 으로 관리합니다.
② <select> 태그의 드롭다운 폼에서 성별을 받고, State(gender) 로 관리합니다.
③ type 이 date 인 <input> 태그의 입력 폼에서 생년월일을 받고, State(birth) 로 관리합니다.
④ <textarea> 태그의 글 상자에서 자기소개 내용을 받고, State(bio) 로 관리합니다.
총 4개의 State 변수와 이벤트 핸드럴를 생성합니다. 저장한 다음, 4개의 입력 폼이 잘 나타나는지, 값은 잘 입력되는지 확인합니다.
사용자로부터 여러 입력 정보를 받아 State 로 처리하는 경우, 관리할 State 의 개수가 많아지면 코드의 길이 또한 길어집니다. 객체 자료형을 이용하면 입력 내용이 여러 가지라도 하나의 State 에서 관리할 수 있어 더 간결하게 코드를 작성할 수 있습니다.
다음과 같이 Body 컴포넌트를 수정하겠습니다.
src/component/Body.js
import { useState } from "react";
function Body() {
const [state, setState] = useState({ ①
name: "",
gender: "",
birth: "",
bio: "",
});
const handleOnChange = (e) => {
console.log("현재 수정 대상:", e.target.name);
console.log("수정값:", e.target.value);
setState({
...state,
[e.target.name]: e.target.value,
});
};
return (
<div>
<div>
<input
name="name" ②
value={state.name} ③
onChange={handleOnChange} ④
placeholder="이름"
/>
</div>
<div>
<select name="gender" value={state.gender} onChange={handleOnChange}>
<option key={""}></option>
<option key={"남성"}>남성</option>
<option key={"여성"}>여성</option>
</select>
</div>
<div>
<input
name="birth"
type="date"
value={state.birth}
onChange={handleOnChange}
/>
</div>
<div>
<textarea name="bio" value={state.bio} onChange={handleOnChange} />
</div>
</div>
);
}
export default Body;
① 객체 자료형으로 State 를 하나 생성하고 초깃값을 설정합니다. State 변수인 객체 state 의 초깃 값은 모두 공백 문자열("") 이며, name, gender, birth, bio 프로퍼티가 있습니다.
② 모든 입력 폼에서 name 속성을 지정합니다. 예를 들어 이름을 입력하는 <input> 태그의 name 속성은 "name" 으로 지정합니다. 나머지 태그들도 name 속성을 추가합니다.
③ 모든 입력 폼의 value 를 객체 state 의 프로퍼티 중 하나로 설정합니다. 예를 들어 이름을 입력하는 <input> 태그의 value 속성에는 state.name 을 지정하는데, 객체 state 의 name 프로퍼티와 동일한 값으로 설정합니다. 나머지 태그에도 value 속성을 이 같이 변경합니다.
④ 사용자의 입력을 처리할 이벤트 핸들러 handleOnChange 를 생성합니다. 나머지 태그에도 동일하게 생성합니다.
④번에서 호출하는 이벤트 핸들러는 이벤트 객체 e 를 매개변수로 저장하고 다음과 같이 setState 를 호출합니다.
setState({
...state,
[e.target.name]: e.target.value,
});
함수 setState 에서는 새로운 객체를 생성해 전달합니다. 이때 스프레드 연산자를 이용해 기존 객체 state 의 값을 나열합니다. 그리고 객체의 괄호 표기법을 사용해 입력 폼의 name 속성(e.target.name) 을 key 로, 입력 폼에 입력한 값(e.target.value) 을 value 로 저장합니다.
e.target.name 은 현재 이벤트가 발생한 요소의 name 속성입니다. 예를 들어 성별을 입력하는 <select> 태그에서 onChange 이벤트가 발생했다면, e.target.name 은 gender 가 됩니다. 결국 객체 state 의 4가지 프로퍼티 중 현재 이벤트가 발생한 요소인 gender 프로퍼티의 value 값을 변경하게 됩니다. 객체 자료형을 이용하면 하나의 State 로 여러 개의 입력을 동시에 관리할 수 있습니다. 저장한 다음, 페이지에서 이름은 '1', 성별은 '남성', 날짜는 오늘 날짜, 자기소개에는 '1' 을 각각 입력한 다음 콘솔에서 확인합니다.
콘솔에서 다음 그림과 같은 결과가 나왔다면 정상적으로 동작하는 겁니다.
Props 와 State
동적으로 변하는 값인 리액트의 State 역시 일종의 값이므로 Props 로 전달할 수 있습니다. 이번에는 Body 에 자식 컴포넌트를 만들고, Body 의 State 를 Props 로 전달합니다. 다음과 같이 Body.js 를 수정합니다.
src/component/Body.js
import "./Body.css";
import { useState } from "react";
function Viewer({ number }) {
return <div>{number % 2 === 0 ? <h3>짝수</h3> : <h3>홀수</h3>}</div>;
}
function Body() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(number + 1);
};
const onDecrease = () => {
setNumber(number - 1);
};
return (
<div>
<h2>{number}</h2>
<Viewer number={number} />
<div>
<button onClick={onDecrease}>-</button>
<button onClick={onIncrease}>+</button>
</div>
</div>
);
}
export default Body;
① Viewer 컴포넌트를 선언합니다. 이 컴포넌트에는 Props 로 Body 컴포넌트에 있는 State 변수 number 가 전달됩니다. Viewer 컴포넌트는 조건부 렌더링을 이용해 변수 number 의 값을 평가하고, 값에 따라 짝수 또는 홀수 값을 페이지에 렌더링합니다.
② Body 에서 Viewer 를 자식 컴포넌트로 사용하며, Props 로 변수 number 를 전달합니다.
코드를 저장하고 페이지에서 결과를 확인합니다. < + > 버튼과 < - > 버튼을 클릭해 값이 어떻게 변하는지 확인합니다.
여기서 알 수 있는 중요한 사실이 있습니다. 바로 자식 컴포넌트는 Props 로 전달된 State 값이 변하면 자신도 리렌더된다는 사실입니다. 즉, 부모에 속해 있는 State(number) 값이 변하면 Viewer 컴포넌트에서 구현한 '짝수', '홀수' 값도 따라서 변합니다.
State 와 자식 컴포넌트
부모의 State 값이 변하면 해당 State 를 Props 로 받은 자식 컴포넌트 역시 리렌더된다는 걸 알았습니다.
그렇다면 부모 컴포넌트가 자식에세 State 를 Props 로 전달하지 않는 경우는 어떻게 될까요? 그래도 부모 컴포넌트의 State 가 변하면 자식 컴포넌트도 리렌더될까요?
다음과 같이 Body.js 를 수정합니다.
src/comoponent/Body.js
import { useState } from "react";
function Viewer() { ①
console.log("viewer component update!");
return <div>Viewer</div>;
}
function Body() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(number + 1);
};
const onDecrease = () => {
setNumber(number - 1);
};
return (
<div>
<h2>{number}</h2>
<Viewer /> ②
<div>
<button onClick={onDecrease}>-</button>
<button onClick={onIncrease}>+</button>
</div>
</div>
);
}
export default Body;
① Viewer 컴포넌트가 Props 를 받지 않습니다. conosole.log 를 이용해 리렌더될 때마다 viewer component update! 라는 메시지를 콘솔에 출력합니다.
② Body 도 더 이상 Viewer 컴포넌트에 Props 로 State 를 전달하지 않습니다.
수정을 완료했다면 < + > 버튼을 정확히 5번 누른 다음 콘솔을 확인합니다.
콘솔에서 6번의 viewer component update! 가 출력되었습니다. 첫 번째 출력은 Viewer 컴포넌트를 페이지에 처음 렌더링할 때 출력된 것입니다. 나머지 5번은 Body 컴포넌트의 State 가 변할 때마다 출력되었습니다.
리액트에서는 부모 컴포넌트가 리렌더하면 자식도 함께 리렌더됩니다. 사실 지금의 Viewer 는 Body 컴포넌트의 State 가 변한다고 해서 리렌더할 이유가 없습니다. Viewer 컴포넌트의 내용에는 변한 게 없기 때문이죠.
의미 없는 리렌더가 자주 발생하면 웹 브라우저의 성능은 떨어집니다. 따라서 컴포넌트의 부모-자식 관계에서 State 를 사용할 때는 늘 주의가 필요합니다. 리액트에서는 이런 성능 낭비를 막는 최적화 기법이 있는데, 이는 추후에 살펴보겠습니다.
'React.js > 한 입 크기로 잘라먹는 React.js' 카테고리의 다른 글
[P1-1. 카운터 앱 만들기] 프로젝트 준비하기 (0) | 2023.06.02 |
---|---|
REF (0) | 2023.06.02 |
이벤트 처리하기 (0) | 2023.05.26 |
컴포넌트에 값 전달하기 (0) | 2023.05.22 |
JSX (0) | 2023.05.22 |