읽기 전용 객체가 아니라면 props를 전달받은 하위 컴포넌트 내에서 props를 직접 수정 시 props를 전달한 상위 컴포넌트의 값에 영향을 미칠 수 있게 됩니다. 즉, 개발자가 의도하지 않은 side effect가 생기게 되고 이는 React의 단방향, 하향식 데이터 흐름 원칙(React is all about one-way data flow down the component hierarchy)에 위배됩니다.
ES6 class 문법으로도 컴포넌트를 만들 수 있고, 이를 클래스 컴포넌트라고 합니다. 이와 대조해서 함수 문법을 사용하여 만든 컴포넌트를 말 그대로 함수 컴포넌트라고 부릅니다. Hook이 나오기 전에는 state는 클래스 컴포넌트에만 다룰 수 있었으나, Hook이 나오면서 함수 컴포넌트도 state를 다룰 수 있게 되었습니다.
// 0. 위 단계에 맞추어 props를 사용하기 위해 우선 <Parent> 와 <Child> 라는 컴포넌트를 선언하고,
// <Parent> 컴포넌트 안에 <Child> 컴포넌트를 작성
function Parent() {
return (
<div className="parent">
<h1>I'm the parent</h1>
<Child />
</div>
);
};
function Child() {
return (
<div className="child"></div>
);
};
// 1. 하위 컴포넌트에 전달하고자 하는 값과 속성을 정의
// 구문: 부모(상위)컴포넌트 내 코드에 작성 <Child attribute={value} />
function Parent() {
return (
<div className="parent">
<h1>I'm the parent</h1>
{/* text 속성 선언 및 문자열 값을 할당하여 Child 컴포넌트에 전달 */}
<Child text={"I'm the eldest child"} />
</div>
);
};
// 2. 상위 컴포넌트에서 전달한 문자열을 하위 컴포넌트에서 받아오기
// 함수에 인자를 전달하듯 하위 컴포넌트에 props를 전달
function Child(props) {
return (
<div className="child"></div>
);
};
// 3. 전달받은 props를 렌더링
// props는 객체이기 때문에 { 키: 값 } 형태, 사용할 때도 dot notation으로 접근할 수 있음
function Child(props) {
return (
<div className="child">
<p>{props.text}</p> {/* 중괄호로 작성 */}
</div>
);
};
function Parent() {
return (
<div className="parent">
<h1>I'm the parent</h1>
<Child>I'm the eldest child</Child>
</div>
);
};
function Child(props) {
return (
<div className="child">
<p>{props.children}</p>
</div>
);
};
React 16.8 버전에 Hook이 추가되면서 클래스 컴포넌트를 작성하지 않아도 state를 사용할 수 있게 되었습니다.
useState 사용법
useState
를 이용하기 위해 React에서 import
키워드로 불러오기
import { useState } from "react";
useState
를 컴포넌트 안에서 호출
useState
를 호출한다는 것은 "state" 라는 변수를 선언하는 것과 같으며, 이 변수의 이름은 아무 이름으로 지어도 된다. 일반적인 변수는 함수가 끝날 때 사라지지만, state 변수는 React에 의해 함수가 끝나도 사라지지 않는다.// 기본 문법
const [state 저장 변수, state 갱신 함수] = useState(상태 초기 값);
function CheckboxExample() {
// 새로운 state 변수를 선언, 구조 분해 할당 (예시의 변수명: isChecked)
const [isChecked, setIsChecked] = useState(false);
}
// 위 코드를 풀어쓰면 아래와 같음
function CheckboxExample() {
const stateHookArray = useState(false);
const isChecked = stateHookArray[0];
const setIsChecked = stateHookArray[1];
}
isChecked
: state를 저장하는 변수setIsChecked
: state isChecked
를 변경하는 setState함수useState
: state hookfalse
: state 초깃값state 변수에 저장된 값을 사용하려면 JSX 엘리먼트 안에 직접 불러서 사용
// 예시에서 isChecked가 boolean 값을 가지므로 참/거짓 여부에 따라 다른 결과가 보이도록 삼항 연산자 사용
return (
<div className="App">
<input type="checkbox" checked={isChecked} onChange={handleChecked} />
<span>{isChecked ? "Checked!!" : "Unchecked"}</span>
</div>
);
state 갱신하기
function CheckboxExample() {
const [isChecked, setIsChecked] = useState(false);
const handleChecked = (event) => {
setIsChecked(event.target.checked);
};
return (
<div className="App">
<input type="checkbox" checked={isChecked} onChange={handleChecked} />
<span>{isChecked ? "Checked!!" : "Unchecked"}</span>
</div>
);
}
setIsChecked
를 호출input[type=checkbox]
JSX 엘리먼트의 값 변경에 따라서 isChecked
가 변경되어야 하므로, 브라우저에서 checked
로 값이 변경되었다면, React의 isChecked
도 변경되어야함input[type=checkbox]
엘리먼트의 값(체크박스 값)이 변경되면 onChange
이벤트가 발생handleChecked
를 호출하고, 이 함수가 setIsChecked
를 호출setIsChecked
가 호출되면 호출된 결과에 따라 isChecked
변수가 갱신되며, React는 새로운 isChecked
변수를 CheckboxExample
컴포넌트에 넘겨 해당 컴포넌트를 다시 렌더링state hook 사용 시 주의점
state.push(1);
, state[1] = 2;
, state = 'wrong state';
등React의 이벤트 처리(이벤트 핸들링; Event handling) 방식은 DOM의 이벤트 처리 방식과 유사하나, 몇 가지 문법 차이가 있음
// HTML 이벤트 처리 방식
<button onclick="handleEvent()">Event</button>
// React 이벤트 처리 방식
<button onClick={handleEvent}>Event</button>
onChange
<input>
, <textarea>
, <select>
와 같은 폼(Form) 엘리먼트는 사용자의 입력값을 제어하는 데 사용되며, React 에서는 이러한 변경될 수 있는 입력값을 일반적으로 컴포넌트의 state 로 관리하고 업데이트 함onChange
는 input
의 텍스트가 바뀔 때마다 발생하는 이벤트로, onChange
이벤트가 발생하면 handleChange
함수가 작동하며, 이벤트 객체에 담긴 input
값을 setState
(여기선 setName) 를 통해 새로운 state 로 갱신함e.target.value
를 통해 이벤트 객체에 담겨있는 input
값을 읽어올 수 있음function NameForm() {
const [name, setName] = useState("");
const handleChange = (e) => {
setName(e.target.value);
}
return (
<div>
<input type="text" value={name} onChange={handleChange}></input>
<h1>{name}</h1>
</div>
)
};
name
: state를 저장하는 변수setName
: state name
을 변경하는 setState함수useState
: state hook“”
: state 초깃값onClick
onClick
이벤트는 사용자가 ‘클릭’이라는 행동을 하였을 때 발생하는 이벤트
버튼이나 <a>
tag 를 통한 링크 이동 등과 같이 주로 사용자의 행동에 따라 애플리케이션이 반응해야 할 때 자주 사용함
onClick
이벤트에 함수를 전달할 때는 함수를 호출하는 것이 아니라 아래와 같이 리턴문 안에서 함수를 정의하거나 리턴문 외부에서 함수를 정의 후 이벤트에 함수 자체를 전달해야 함
// 함수 정의하기
function NameForm() {
const [name, setName] = useState("");
const handleChange = (e) => {
setName(e.target.value);
}
return (
<div>
<input type="text" value={name} onChange={handleChange}></input>
<button onClick={() => alert(name)}>Button</button>
<h1>{name}</h1>
</div>
// 함수 자체를 전달하기
function NameForm() {
const [name, setName] = useState("");
const handleChange = (e) => {
setName(e.target.value);
}
const handleClick = () => {
alert(name);
};
return (
<div>
<input type="text" value={name} onChange={handleChange}></input>
<button onClick={handleClick}>Button</button>
<h1>{name}</h1>
</div>
);
};
// 잘못된 사용 예시
function NameForm() {
const [name, setName] = useState("");
const handleChange = (e) => {
setName(e.target.value);
}
return (
<div>
<input type="text" value={name} onChange={handleChange}></input>
<button onClick={alert(name)}>Button</button> {/* 잘못된 사용 */}
<h1>{name}</h1>
</div>
);
};
/* onClick 이벤트에 alert(name) 함수를 바로 호출하면 컴포넌트가 렌더링 될 때
함수 자체가 아닌 함수 호출의 결과가 onClick 에 적용되기 때문에 버튼을 클릭할 때가 아닌,
컴포넌트가 렌더링 될 때에 alert이 실행됨 → 함수 호출 결과: 리턴 값이 없으므로 undefined 반환*/
// onClick(undefined) → 아무런 결과도 일어나지 않음
<select>
select tag
는 사용자가 drop down 목록을 열어 그중 한 가지 옵션을 선택하면, 선택된 옵션이 state 변수에 갱신됩니다. useState
가 어떠한 상태를 갱신하도록 설정되어 있는지 확인한 뒤, 주석에 따라 select tag
가 정상적으로 작동하도록 코드를 완성하세요.
function SelectExample() {
const [choice, setChoice] = useState("apple");
const fruits = ["apple", "orange", "pineapple", "strawberry", "grape"];
const options = fruits.map((fruit) => {
return <option value={fruit}>{fruit}</option>;
});
console.log(choice);
const handleFruit = (event) => {
//TODO : select tag 가 정상적으로 작동하도록 코드를 완성하세요.
setChoice(event.target.value)
};
return (
<div className="App">
<select onChange={handleFruit}>{options}</select>
<h3>You choose "{choice}"</h3>
</div>
);
}
Pop up
Pop up 역시 Pop up 의 open 과 close 를 state 를 통해 관리할 수 있습니다. 이 예제 또한 useState
를 확인하여 버튼 클릭에 따라 Pop up 이 open/close 되도록 코드를 완성하세요.
function App() {
const [showPopup, setShowPopup] = useState(false);
const togglePopup = () => {
// Pop up 의 open/close 상태에 따라 현재 state 가 업데이트 되도록 함수를 완성하세요.
if (!showPopup) { // showPopup이 false이면,
setShowPopup(true); // state를 true로 업데이트
} else {
setShowPopup(false); // 아니면(true이면) false로 업데이트
}
};
return (
<div className="App">
<h1>Fix me to open Pop Up</h1>
{/* 버튼을 클릭했을 때 Pop up 의 open/close 가 작동하도록
button tag를 완성하세요. */}
<button className="open" onClick={togglePopup}>Open me</button>
{showPopup ? (
<div className="popup">
<div className="popup_inner">
<h2>Success!</h2>
<button className="close" onClick={togglePopup}>
Close me
</button>
</div>
</div>
) : null}
</div>
);
}
React가 state를 통제할 수 있는 컴포넌트이다.
input에 값 입력 시, state도 그때그때 바뀌고(onChange), 변경된 state와 input의 value가 같게 작성하면 된다.
import React, { useState } from "react";
export default function App() {
const [username, setUsername] = useState("");
const [msg, setMsg] = useState("");
return (
<div className="App">
<div>{username}</div>
{/* input, textarea에 입력된 내용이 바뀌면
이벤트 핸들러와 setState 함수를 통해 state를 업데이트 해 주고,
value에 state 값을 넣어준다. */}
<input
type="text"
onChange={(event) => setUsername(event.target.value)}
value={username}
placeholder="여기는 인풋입니다."
className="tweetForm__input--username"
></input>
<div>{msg}</div>
<textarea
placeholder="여기는 텍스트 영역입니다."
className="tweetForm__input--message"
onChange={(event) => setMsg(event.target.value)}
value={msg}
></textarea>
</div>
);
}