빙응의 공부 블로그
[React]리액트 - Form, Lifting State Up, Inheritance, Context 본문
📝 Forms
폼 = 양식
사용자로부터 입력을 받는 것의 총칭이다.
HTML Form
<form>
<label>
이름
<input type="text" name="name"/>
</label>
<button type="submit">제출</button>
</form>
🚩 리액트의 Controlled Component
import React, { useState } from "react";
function NameForm(props){
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('입력한 이름: ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
이름:
<input type="text" value={value} onChange={handleChange}/>
</label>
<button type="submit">제출</button>
</form>
)
}
해당처럼 useState를 사용하여 폼을 제어한다.
🚩 다양한 폼
Textarea 태그
import React, { useState } from "react";
function NameForm(props){
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('입력한 이름: ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
이름:
<textarea value={value} onChange={handleChange}/>
</label>
<button type="submit">제출</button>
</form>
)
}
SELECT
import React, { useState } from "react";
function NameSelect(props){
const [value, setValue] = useState('grape');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('입력한 이름: ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
이름:
<select value={value} onChange={handleChange}>
<option value="apple">사과</option>
<option value="banana">바나나</option>
<option value="grape">포도</option>
<option value="watermelon">수박</option>
</select>
</label>
<button type="submit">제출</button>
</form>
)
}
File input 태그
하나 도는 여러 개의 파일을 선택할 수 있게 해주는 HTML 태그이다.
<input type="file"/>
📝Lifting State Up
컴포넌트 사이에서 State를 공유하는 법을 알아보자
🚩 Shared State
자식 컴포넌트들이 공통된 부모 컴포넌트의 State를 공유하는 것을 말한다.
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>물이 끓습니다.</p>;
}
return <p>물이 끓지 않습니다.</p>;
}
function toCelsius(fahrenheit) {
return ((fahrenheit - 32) * 5) / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9) / 5 + 32;
}
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return "";
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
function Calculator(props) {
const [temperature, setTemperature] = useState("");
const [scale, setScale] = useState("c");
const handleCelsiusChange = (temperature) => {
setTemperature(temperature);
setScale("c");
};
const handleFahrenheitChange = (temperature) => {
setTemperature(temperature);
setScale("f");
};
const celsius =
scale === "f" ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit =
scale === "c" ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={handleCelsiusChange}
/>
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={handleFahrenheitChange}
/>
<BoilingVerdict celsius={parseFloat(celsius)} />
</div>
);
}
export default Calculator;
const scaleNames = {
c: "섭씨",
f: "화씨",
};
function TemperatureInput(props) {
const handleChange = (event) => {
props.onTemperatureChange(event.target.value);
};
return (
<fieldset>
<legend>
온도를 입력해주세요(단위:{scaleNames[props.scale]}):
</legend>
<input value={props.temperature} onChange={handleChange} />
</fieldset>
);
}
export default TemperatureInput;
- 위 코드처럼 하위 모듈을 상위에서 정의하여 하위 컴포넌트끼리 서로 부모 컴포넌트의 State를 공유한다.
📝 Composition VS Inheritance
🚩 Composition
Composition : 여러 개의 컴포넌트를 합쳐서 새로운 컴포넌트를 만드는 것
Containment
- 하위 컴포넌트를 포함하는 형태의 합성 방법
- props의 children을 사용하는 것
function FancyBorder(props){
return(
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
)
}
FancyBorder 컴포넌트 안에 있는 모든 JSX 태그는 children으로 전달된다.
그렇기에 Containment는 child를 받아 부모 컴포넌트에서 합성하듯 렌더링하는 방식이다.
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
function App() {
return (
<Card>
<h2>제목</h2>
<p>내용 내용 내용</p>
</Card>
);
}
전문화, 특수화
범용적인 상황이 아닌 구체적인 상황에 대한 대응을 하는 구현이다.
리액트에서는 합성을 사용하여 특수화를 구현한다.
function Dialog(props){
return (
<FancyBorder coler = "blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog(props){
return (
<Dialog
title="어서오세요"
message="우리 사이트에 방문하신 것을 환영합니다!"
/>
);
}
특수화는 범용적인 컴포넌트를 만들어놓고 특수한 상황에 대한 것을 만들면 된다.
🚩 Inheritance
상속의 의미로
객체지향에서 나온 의미이다.
다른 컴포넌트로부터 상속을 받아서 새로운 컴포넌트를 만드는 것이다.
- 리액트에서는 잘 사용하지 않는다.
복잡한 컴포넌트를 쪼개서
여러 개의 컴포넌트로 만들고, 만든 컴포넌트를 조합해서
새로운 컴포넌트를 만들자!
📝Context
컴포넌트는 props를 통해 데이터를 단방향 전달을 한다.
그러나 이것이 너무 많아지면 코드가 복잡해지고 효율성이 떨어진다.
Context는 props로 내려가면 전달하는 것이 아닌 곧바로 데이터를 전달한다.
사용 범위
- 여러 개의 컴포넌트가 접근하는 데이터
- 로그인 여부, 로그인 정보, UI 테마, 현재 언어 등등
사용 예제
이처럼 테마 하나를 위해 깊이가 깊은 코드를 내려가야 한다. 이것을 해결하는 것이 Context이다.
const ThemeContext = React.createContext("light");
function App(props){
return (
<ThemeContext.Provider value="dark">
<Toolbar/>
</ThemeContext.Provider>
);
}
function Toolbar(props){
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton(props){
return (
<ThemeContext.Consumer>
{value=> <button theme={value}/>}
</ThemeContext.Consumer>
);
}
사용하기 전에 고려할 점
무조건 Context를 사용하는 것이 좋은 것은 아니다.
- 다른 레벨에 많은 컴포넌트가 특정 데이터를 필요로 할때 사용해야 한다.
- 재사용성이 떨어지기 때문
📝 Context API
리액트가 제공하는 Context에 대해 알아보자
React.createContext()
const MyContext = React.createContext(기본값);
- 상위 레벨에 매칭되는 Provider가 없다면 기본값이 사용된다.!
Context.Provider
하위 컴포넌트에게 데이터를 제공해주는 컴포넌트를 지정한다.
<MyContext.Provider value = {}>
- Provider의 value는 하위 컴포넌트에게 제공된다.
- Provider의 value가 바뀌면 하위 컴포넌트는 재렌더링 된다.
🚩 주의점
- Provider 컴포넌트가 재렌더링될 때마다 모든 하위 consumer 컴포넌트가 재렌더링된다.
- 의도치 않은 재렌더링이 될 수 있다.
function App(props){
return (
<MyContext.Provider value={{something : 'someThing'}}>
<Toolbar/>
</MyContext.Provider>
);
}
- 위 코드는 해당 컴포넌트가 재렌더링 될 때마다 모든 하위 컴포넌트가 재렌더링된다.
- 그 이유는 something 벨류가 매번 새롭게 생성되기 때문이다.
function App(props){
const [value,setValue] = useState({something: 'something'})
return (
<MyContext.Provider value={value}>
<Toolbar/>
</MyContext.Provider>
);
}
state를 사용해서 불필요한 랜더링을 막을 수 있다.
Context.Consumer
상위 컴포넌트의 Context를 구독하여 사용할 수 있게 한다.
<MyContext.Comsumer>
{value => /* */}
</MyContext.Consumer>
- 만약 상위 Provider가 없다면 기본값을 사용한다.
Context.displayName
- 개발자 도구에서의 이름을 지정한다.
'React' 카테고리의 다른 글
[React]리액트 - CSS (0) | 2024.05.13 |
---|---|
[React]리액트 - Event, Condition, List and Keys (0) | 2024.05.10 |
[React]리액트 -State and Lifecycle, hooks (0) | 2024.05.09 |
[React]리액트 - Elements, Components, Props (0) | 2024.05.07 |
[React]리액트란 무엇인가? (1) | 2024.05.06 |