상태변수가 언제 바뀌느냐 !
컴포넌트 내부에서 이벤트에 의한 값의 변화
<input type="text" name="message" placeholder="입력해 보세요."
onChange={e => console.log(e.target.value)} />
e를 출력하면
taget > value 가 내가 입력한 값이다.
그래서 e.target.value 로 많이 쓴다.
import { Component } from "react";
class EventPractice extends Component {
state = {
message: ""
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
onChange={e => {
console.log(e.target.value);
this.setState({ message: e.target.value })
}}/>
<h2>입력창 내용: {this.state.message}</h2>
</div>
)
}
}
export default EventPractice;
import { Component } from "react";
class EventPractice extends Component {
state = {
message: ""
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={this.state.message} // 해당 라인이 없으면 삭제 버튼을 클릭해도 입력창 내용은 지워지지 않는다
onChange={e => {
console.log(e.target.value);
this.setState({ message: e.target.value })
}}/>
<h2>입력창 내용: {this.state.message}</h2>
<button onClick={() => this.setState({ message: "" })}>삭제</button>
</div>
)
}
}
export default EventPractice;
value 에 this.state.message 를 넣어줘서 상태를 함께해줘야 삭제 버튼을 누른 것이 입력창에도 반영이 된다.
이벤트 핸들러 함수 ⬇️
e => {
console.log(e.target.value);
this.setState({ message: e.target.value })
}}
() => this.setState({ message: "" })
이렇게 함수를 외부에 정의해서 jsx 구문에서는 간단하게 나타낼 수 있다.⭐️ ⬇️
import { Component } from "react";
class EventPractice extends Component {
state = {
message: ""
}
handlerChange = e => {
console.log(e.target.value);
this.setState({ message: e.target.value });
};
handlerClick = () => {
this.setState({ message: "" });
};
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={this.state.message}
onChange={this.handlerChange}/>
<h2>입력창 내용: {this.state.message}</h2>
<button onClick={this.handlerClick}>삭제</button>
</div>
)
}
}
export default EventPractice;
화살표 함수로 사용하면 this 바인딩을 안 해줘도 돼서 화살표 함수로 사용해주면 된다.
import { Component } from "react";
class EventPractice extends Component {
state = {
message: "",
username: ""
}
handlerChangeMessage = e => {
console.log(e.target.value);
this.setState({ message: e.target.value });
};
handlerChangeUsername = e => {
console.log(e.target.value);
this.setState({ username: e.target.value });
};
handlerClick = () => {
this.setState({ message: "", username: "" });
};
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={this.state.message}
onChange={this.handlerChangeMessage}/>
<input type="text" name="username" placeholder="입력해 보세요."
value={this.state.username}
onChange={this.handlerChangeUsername}/>
<h2>message: {this.state.message}</h2>
<h2>username: {this.state.username}</h2>
<button onClick={this.handlerClick}>삭제</button>
</div>
)
}
}
export default EventPractice;
❓ 입력창이 10개 있으면 10개의 메서드가 만들어져야 할텐데,, 줄일 수 없을까?
💡 input 의 name 과 setState 안에 들어있는 키 값이 같다!
계산된 속성명을 사용해보자!!
handlerChange = (e) => {
// 계산된 속성명 (어떤 변수가 가지고 있는 값을 객체의 키 이름으로 사용하겠다)
this.setState({ [e.target.name]: e.target.value });
};
import { Component } from "react";
class EventPractice extends Component {
state = {
message: "",
username: ""
}
// handlerChangeMessage = e => {
// console.log(e.target.value);
// this.setState({ message: e.target.value });
// };
// handlerChangeUsername = e => {
// console.log(e.target.value);
// this.setState({ username: e.target.value });
// };
handlerChange = (e) => {
// 계산된 속성명 (어떤 변수가 가지고 있는 값을 객체의 키 이름으로 사용하겠다)
this.setState({ [e.target.name]: e.target.value });
};
handlerClick = () => {
this.setState({ message: "", username: "" });
};
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={this.state.message}
onChange={this.handlerChange}/>
<input type="text" name="username" placeholder="입력해 보세요."
value={this.state.username}
onChange={this.handlerChange}/>
<h2>message: {this.state.message}</h2>
<h2>username: {this.state.username}</h2>
<button onClick={this.handlerClick}>삭제</button>
</div>
)
}
}
export default EventPractice;
handlerKeyUp = (e) => {
console.log(e)
if (e.key === "Enter") {
this.handlerClick();
}
}
e.key 를 보면 된다 !!
import { Component } from "react";
class EventPractice extends Component {
state = {
message: "",
username: ""
}
handlerChange = (e) => {
// 계산된 속성명 (어떤 변수가 가지고 있는 값을 객체의 키 이름으로 사용하겠다)
this.setState({ [e.target.name]: e.target.value });
};
handlerClick = () => {
this.setState({ message: "", username: "" });
};
handlerKeyUp = (e) => {
console.log(e)
if (e.key === "Enter") {
this.handlerClick();
}
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={this.state.message}
onChange={this.handlerChange}
onKeyUp={this.handlerKeyUp}
/>
<input type="text" name="username" placeholder="입력해 보세요."
value={this.state.username}
onChange={this.handlerChange}
onKeyUp={handlerKeyUp}
/>
<h2>message: {this.state.message}</h2>
<h2>username: {this.state.username}</h2>
<button onClick={this.handlerClick}>삭제</button>
</div>
)
}
}
export default EventPractice;
지금까지 구현했던 내용을 함수형 컴포넌트로 변경해보자 !!
import {useState} from "react";
function EventPractice() {
const [message, setMessage] = useState("");
const [username, setUsername] = useState("");
const handlerChangeMessage = e => {
setMessage(e.target.value)
};
const handlerChangeUsername = e => {
setUsername(e.target.value)
};
const handlerClick = () => {
setMessage("");
setUsername("");
};
const handlerKeyUp = e => {
if (e.key === "Enter") {
handlerClick();
}
};
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={message}
onChange={handlerChangeMessage}
onKeyUp={handlerKeyUp}
/>
<input type="text" name="username" placeholder="입력해 보세요."
value={username}
onChange={handlerChangeUsername}
onKeyUp={handlerKeyUp}
/>
<h2>message: {message}</h2>
<h2>username: {username}</h2>
<button onClick={handlerClick}>삭제</button>
</div>
)
}
export default EventPractice;
더 간략해졌다 !
import {useState} from "react";
function EventPractice() {
const [message, setMessage] = useState("");
const [username, setUsername] = useState("");
/*
const handlerChangeMessage = e => {
setMessage(e.target.value)
};
const handlerChangeUsername = e => {
setUsername(e.target.value)
};
*/
const handlerChange = e => {
if (e.target.name === "message") {
setMessage(e.target.value);
} else if (e.target.name === "username") {
setUsername(e.target.value);
}
}
const handlerClick = () => {
setMessage("");
setUsername("");
};
const handlerKeyUp = e => {
if (e.key === "Enter") {
handlerClick();
}
};
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={message}
onChange={handlerChange}
onKeyUp={handlerKeyUp}
/>
<input type="text" name="username" placeholder="입력해 보세요."
value={username}
onChange={handlerChange}
onKeyUp={handlerKeyUp}
/>
<h2>message: {message}</h2>
<h2>username: {username}</h2>
<button onClick={handlerClick}>삭제</button>
</div>
)
}
export default EventPractice;
이렇게 통합은 되었는데 찜찜하다,, 입력창이 많아지면 else if 가 너무 늘어날 것 같다.
와 이거 진짜 유용한듯 ㅜㅜ 최고다
/* 이거 대신
const [message, setMessage] = useState("");
const [username, setUsername] = useState("");
*/
const [form, setForm] = useState({ message: "", username: "" });
// 객체 비구조화
const { message, username } = form;
이렇게 바꿔버리기 !!!! 객체 비구조화를 했기 때문에 value 값도 안 건들여도 된다.
const handlerChange = e => {
/* 이거 대신
if (e.target.name === "message") {
setMessage(e.target.value);
} else if (e.target.name === "username") {
setUsername(e.target.value);
}
*/
// 전개 연산자를 이용해서 form 객체의 사본을 만들고, 그 사본에 변경을 반영
const newForm = { ...form, [e.target.name]: e.target.value };
// setter 함수를 이용해서 사본을 저장
setForm(newForm);
}
나머지는 그대로 !!
확장성이 아주 좋아진 코드가 되었다.
email 추가까지 한 전체 코드 ⬇️
import {useState} from "react";
function EventPractice() {
/*
const [message, setMessage] = useState("");
const [username, setUsername] = useState("");
*/
const [form, setForm] = useState({ message: "", username: "", email: "" });
// 객체 비구조화
const { message, username, email } = form;
const handlerChange = e => {
/*
if (e.target.name === "message") {
setMessage(e.target.value);
} else if (e.target.name === "username") {
setUsername(e.target.value);
}
*/
const newForm = { ...form, [e.target.name]: e.target.value };
setForm(newForm);
}
const handlerClick = () => {
/*
setMessage("");
setUsername("");
*/
// 전개 연산자를 이용해서 form 객체의 사본을 만들고, 그 사본에 변경을 반영
const newForm = { message: "", username: "", email: "" };
// setter 함수를 이용해서 사본을 저장
setForm(newForm);
};
const handlerKeyUp = e => {
if (e.key === "Enter") {
handlerClick();
}
};
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={message}
onChange={handlerChange}
onKeyUp={handlerKeyUp}
/>
<input type="text" name="username" placeholder="입력해 보세요."
value={username}
onChange={handlerChange}
onKeyUp={handlerKeyUp}
/>
<input type="text" name="email" placeholder="입력해 보세요."
value={email}
onChange={handlerChange}
onKeyUp={handlerKeyUp}
/>
<h2>message: {message}</h2>
<h2>username: {username}</h2>
<h2>email: {email}</h2>
<button onClick={handlerClick}>삭제</button>
</div>
)
}
export default EventPractice;
IterationSample.js 파일을 생성하고 App.js에 추가
const IterationSample = () => {
return (
<ul>
<li>봄</li>
<li>여름</li>
<li>가을</li>
<li>겨울</li>
</ul>
);
};
export default IterationSample;
const IterationSample = () => {
const words = ["봄", "여름", "가을", "겨울"];
const wordList =words.map(word => <li>{word}</li>);
return <ul>{wordList}</ul>
};
export default IterationSample;
🚨 key 값이 없다ㅜ
const IterationSample = () => {
const words = ["봄", "여름", "가을", "겨울"];
const wordList =words.map((word, index) => <li key={index}>{word}</li>);
return <ul>{wordList}</ul>
};
export default IterationSample;
보통 데이터는 객체의 배열 넘어오고 id값이 있으니 이를 활용해보자 !
const IterationSample = () => {
const words = [
{ id: 1, word: "봄" },
{ id: 2, word: "여름" },
{ id: 3, word: "가을" },
{ id: 4, word: "겨울" },
];
const wordList =words.map(item => <li key={item.id}>{item.word}</li>);
return <ul>{wordList}</ul>
};
export default IterationSample;
import { useState } from "react";
const IterationSample = () => {
// 목록 데이터를 관리하는 상태변수
const [words, setWords] = useState([
{ id: 1, word: "봄" },
{ id: 2, word: "여름" },
{ id: 3, word: "가을" },
{ id: 4, word: "겨울" },
]);
const wordList =words.map(item => <li key={item.id}>{item.word}</li>);
// 입력창 내용을 관리하는 상태변수
const [inputText, setInputText] = useState("");
return (
<>
<input type="text" value={inputText} />
<button>추가</button>
<ul>{wordList}</ul>
</>
);
};
export default IterationSample;
상태변수에 객체의 배열을 담아 초기화해주었다 !
// 이벤트 핸들러
const handleChange = e => setInputText(e.target.value);
...
<input type="text" value={inputText} onChange={handleChange}/>
사본 만드는 스프레드 연산자를 사용해도 되고
const newWords = [ ...words, { id: 10, word: inputText } ];
concat 함수는 붙여서 새 배열을 반환하므로 이것도 마찬가지로 활용해도 된다
const newWords = words.concat({ id: 100, word: inputText });
// id를 관리하는 상태변수
const [nextId, setNextId] = useState(5);
const handleAddItem = () => {
// const newWords = [ ...words, { id: nextId, word: inputText } ]; // 이렇게 해도 되고
const newWords = words.concat({ id: nextId, word: inputText });
setWords(newWords);
setNextId(nextId + 1);
setInputText("");
};
...
<button onClick={handleAddItem}>추가</button>
전체 코드 ⬇️
import { useState } from "react";
const IterationSample = () => {
// 목록 데이터를 관리하는 상태변수
const [words, setWords] = useState([
{ id: 1, word: "봄" },
{ id: 2, word: "여름" },
{ id: 3, word: "가을" },
{ id: 4, word: "겨울" },
]);
const wordList =words.map(item => <li key={item.id}>{item.word}</li>);
// 입력창 내용을 관리하는 상태변수
const [inputText, setInputText] = useState("");
// id를 관리하는 상태변수
const [nextId, setNextId] = useState(5);
// 이벤트 핸들러
const handleChange = e => setInputText(e.target.value);
const handleAddItem = () => {
// const newWords = [ ...words, { id: nextId, word: inputText } ]; // 이렇게 해도 되고
const newWords = words.concat({ id: nextId, word: inputText });
setWords(newWords);
setNextId(nextId + 1);
setInputText("");
};
return (
<>
<input type="text" value={inputText} onChange={handleChange}/>
<button onClick={handleAddItem}>추가</button>
<ul>{wordList}</ul>
</>
);
};
export default IterationSample;
const wordList =words.map(item => <li key={item.id} onDoubleClick={() => handleDoubleClick(item.id)}>{item.word}</li>);
// 삭제 - filter
const handleDoubleClick = id => {
const newWords = words.filter(item => item.id !== id);
setWords(newWords);
}
여기서 filter 를 사용해서 해당 item의 id값이 아닌 것만 필터링 해주면 삭제 기능이 된다.
onDoubleClick={() => handleDoubleClick(item.id)}
이 함수 실행에 필요한 이벤트 객체가 아닌 다른 매개변수가 필요하다면
handleDoubleClick
만 써주는 것이 아니라
() => handleDoubleClick(item.id)
이렇게 또다른 익명 함수를 정의해줘야 한다.
최종 코드 ⬇️
import { useState } from "react";
const IterationSample = () => {
// 목록 데이터를 관리하는 상태변수
const [words, setWords] = useState([
{ id: 1, word: "봄" },
{ id: 2, word: "여름" },
{ id: 3, word: "가을" },
{ id: 4, word: "겨울" },
]);
const wordList =words.map(item => <li key={item.id} onDoubleClick={() => handleDoubleClick(item.id)}>{item.word}</li>);
// 입력창 내용을 관리하는 상태변수
const [inputText, setInputText] = useState("");
// id를 관리하는 상태변수
const [nextId, setNextId] = useState(5);
// 이벤트 핸들러
const handleChange = e => setInputText(e.target.value);
// 추가 - concat
const handleAddItem = () => {
// const newWords = [ ...words, { id: nextId, word: inputText } ]; // 이렇게 해도 되고
const newWords = words.concat({ id: nextId, word: inputText });
setWords(newWords);
setNextId(nextId + 1);
setInputText("");
};
// 삭제 - filter
const handleDoubleClick = id => {
const newWords = words.filter(item => item.id !== id);
setWords(newWords);
}
return (
<>
<input type="text" value={inputText} onChange={handleChange}/>
<button onClick={handleAddItem}>추가</button>
<ul>{wordList}</ul>
</>
);
};
export default IterationSample;
onDoubleClick={handleDoubleClick}
이처럼 여기엔 함수의 이름이 와야하는데
onDoubleClick={handleDoubleClick(item.id)}
매개변수를 넣어 함수를 실행하고 있으니까 오류가 난다.
onDoubleClick={() => handleDoubleClick(item.id)}
그래서 매개변수를 넣어주려면 함수를 여기서 다시 정의해줘야 한다.
부모 컴포넌트의 상태 변수를 자식 컴포넌트의 props로 전달
부모 컴포넌트의 상태 변수가 변경되면 부모 컴포넌트, 자식 컴포넌트 모두 다시 렌더링된다.
이걸 해보자 ! ⬇️
<Parent>
<input type="number" /> ⇐ 값이 바뀌면,
<ChildA /> ⇐ props로 전달된 값에 2를 곱한 값이 출력
<ChildB /> ⇐ props로 전달된 값에 3을 곱한 값이 출력
</Parent>
App.js
import { useState } from "react";
function ChildB({value}) {
return (
<p>{value * 3}</p>
)
}
function ChildA({count}) {
return (
<p>{count * 2}</p>
)
}
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<input type="number" value={count} onChange={e => setCount(e.target.value)}/>
<ChildA count={count}/>
<ChildB value={count}/>
</>
)
}
function App() {
return (
<>
<Parent />
</>
);
}
export default App;
function ChildA(count) { ... }
이렇게 하면 NaN 이 떴었는데 객체 비구조화를 안 해서 그렇다ㅜ
count가 객체이기 때문에 중괄호를 안 넣으면 사용할 때 count.count 이런식으로 사용해야 한다.
function ChildA({count}) { ... }
이렇게 중괄호를 넣어서 사용해주자
자식 컴포넌트의 변경 사항을 부모 컴포넌트로 전달하는 방법
부모 컴포넌트에서 정의한 함수를 자식 컴포넌트의 props 변수로 전달, 해당 함수를 자식 컴포넌트에서 이벤트 핸들러로 지정
이걸 해보자 ! ⬇️
<Parent>
<h1>counter</h1>
<Child>
<button>하나 증가</button>
</Child>
<Parent>
import { useState } from "react";
function Child({ increment }) {
return <button onClick={increment}>하나증가</button>;
}
function Parent() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return (
<>
<h1>{count}</h1>
<Child increment={increment}/>
</>
)
}
function App() {
return (
<Parent />
);
}
export default App;
내 실습 코드 ⬇️
import { useState } from "react";
function Child({ increment, decrement }) {
return (
<>
<button onClick={increment}> +1 </button>
<button onClick={decrement}> -1 </button>
</>
)
}
function Parent() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const resetCount = () => setCount(0);
return (
<>
<div>{count}</div>
<button onClick={resetCount}>Reset</button>
<Child increment={increment} decrement={decrement}/>
</>
)
}
function App() {
return (
<Parent />
);
}
export default App;
강사님 코드 ⬇️
import { useState } from "react";
function Child({ addCount, subCount }) {
return (
<>
<button onClick={addCount}> +1 </button>
<button onClick={subCount}> -1 </button>
</>
)
}
function Parent() {
const [count, setCount] = useState(0);
const addCount = () => setCount(count + 1);
const subCount = () => setCount(count - 1);
const resetCount = () => setCount(0);
return (
<>
<div>{count}</div>
<button onClick={resetCount}>Reset</button>
<Child addCount={addCount} subCount={subCount}/>
</>
)
}
function App() {
return (
<Parent />
);
}
export default App;
import { useState } from "react";
function Controller({ plusOne, minusOne }) {
return (
<>
<button onClick={plusOne}> +1 </button>
<button onClick={minusOne}> -1 </button>
</>
)
}
function Display({ count }) {
return (
<h1>{count}</h1>
)
}
function Parent() {
const [count, setCount] = useState(0);
const reset = () => setCount(0);
const plusOne = () => setCount(count + 1);
const minusOne = () => setCount(count - 1);
return (
<>
<button onClick={reset}>Reset</button>
<Controller plusOne={plusOne} minusOne={minusOne}/>
<Display count={count}/>
</>
)
}
function App() {
return (
<Parent />
);
}
export default App;
화씨 = (섭씨 9 / 5) + 32;
섭씨 = (화씨 - 32) 5 / 9;
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <div>물이 끓습니다.</div>
} else {
return <div>물이 끓지 않습니다.</div>
}
}
function App() {
return (
<>
<BoilingVerdict celsius={100} />
<BoilingVerdict celsius={99} />
</>
);
}
export default App;
import { useState } from "react";
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <div>물이 끓습니다.</div>
} else {
return <div>물이 끓지 않습니다.</div>
}
}
function Calculator() {
const [temperature, setTemperature] = useState(0);
const handleChage = e => setTemperature(e.target.value);
return (
<>
<fieldset>
<legend>섭씨 온도를 입력하세요.</legend>
<input value={temperature} onChange={handleChage} />
<BoilingVerdict celsius={temperature}/>
</fieldset>
</>
)
}
function App() {
return (
<>
<Calculator />
</>
);
}
export default App;
import { useState } from "react";
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <div>물이 끓습니다.</div>
} else {
return <div>물이 끓지 않습니다.</div>
}
}
const scaleName = {
c: "섭씨",
f: "화씨"
};
function Temperature({ scale }) {
const [temperature, setTemperature] = useState(0);
const handleChage = e => setTemperature(e.target.value);
return (
<fieldset>
<legend>온도를 입력하세요. (단위: {scaleName[scale]} </legend>
<input value={temperature} onChange={handleChage} />
</fieldset>
)
}
function Calculator() {
return (
<>
<Temperature scale="c" />
<Temperature scale="f" />
{/*
<BoilingVerdict celsius={temperature}/>
*/}
</>
)
}
function App() {
return (
<>
<Calculator />
</>
);
}
export default App;
import { useState } from "react";
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <div>물이 끓습니다.</div>
} else {
return <div>물이 끓지 않습니다.</div>
}
}
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();
}
const scaleName = {
c: "섭씨",
f: "화씨"
};
function Temperature({ scale }) {
const [temperature, setTemperature] = useState(0);
const handleChage = e => setTemperature(e.target.value);
return (
<fieldset>
<legend>온도를 입력하세요. (단위: {scaleName[scale]} </legend>
<input value={temperature} onChange={handleChage} />
</fieldset>
)
}
function Calculator() {
return (
<>
<Temperature scale="c" />
<Temperature scale="f" />
{/*
<BoilingVerdict celsius={temperature}/>
*/}
</>
)
}
function App() {
return (
<>
<Calculator />
</>
);
}
export default App;
import { useState } from "react";
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <div>물이 끓습니다.</div>
} else {
return <div>물이 끓지 않습니다.</div>
}
}
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();
}
const scaleName = {
c: "섭씨",
f: "화씨"
};
function Temperature({ scale }) {
const [temperature, setTemperature] = useState(0);
const handleChage = e => setTemperature(e.target.value);
return (
<fieldset>
<legend>온도를 입력하세요. (단위: {scaleName[scale]}) </legend>
<input value={temperature} onChange={handleChage} />
</fieldset>
)
}
function Calculator() {
const [temperature, setTemperature] = useState("");
const [scale, setScale] = useState("c");
// 화씨 온도가 입력된 경우 호출될 함수
const changeFahrenheit = e => {
setTemperature(e);
setScale("f");
}
// 섭씨 온도가 입력된 경우 호출될 함수
const changeCelsius = e => {
setTemperature(e);
setScale("c");
}
return (
<>
<Temperature scale="c" changeTemperature={changeCelsius} />
<Temperature scale="f" changeTemperature={changeFahrenheit} />
{/*
<BoilingVerdict celsius={temperature}/>
*/}
</>
)
}
function App() {
return (
<>
<Calculator />
</>
);
}
export default App;
최종 코드
import { useState } from "react";
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <div>물이 끓습니다.</div>
} else {
return <div>물이 끓지 않습니다.</div>
}
}
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();
}
const scaleName = {
c: "섭씨",
f: "화씨"
};
function Temperature({ scale, changeTemperature, temperature }) {
// 변경된 내용을 부모 컴포넌트로 전달하므로 상태변수를 유지할 필요가 없다.
// const [temperature, setTemperature] = useState(0);
// const handleChage = e => setTemperature(e.target.value);
const handleChange = e => changeTemperature(e.target.value);
return (
<fieldset>
<legend>온도를 입력하세요. (단위: {scaleName[scale]}) </legend>
<input value={temperature} onChange={handleChange} />
</fieldset>
)
}
function Calculator() {
const [temperature, setTemperature] = useState("");
const [scale, setScale] = useState("c");
// 화씨 온도가 입력된 경우 호출될 함수
const changeFahrenheit = e => {
setTemperature(e);
setScale("f");
}
// 섭씨 온도가 입력된 경우 호출될 함수
const changeCelsius = e => {
setTemperature(e);
setScale("c");
}
const celsius = scale === "f" ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === "c" ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<>
<Temperature scale="c" changeTemperature={changeCelsius} temperature={celsius} />
<Temperature scale="f" changeTemperature={changeFahrenheit} temperature={fahrenheit} />
<BoilingVerdict celsius={celsius}/>
</>
)
}
function App() {
return (
<>
<Calculator />
</>
);
}
export default App;
💡
- scale: 어떤 입력창인지 관리하는 역할. (화씨 입력창인지 섭씨 입력창인지)
- 공통적인 화씨, 섭씨 반올림 로직을 공통으로 적용하기 위해 따로 tryConverter라는 함수를 분리했다!
사용자가 입력한 내용을 server side로 보내는 역할
여러 타입의 input 들이 있을 수 있다.
<form action="내용을 전달할 서버 주소" method="내용을 전달할 방법" enctype="인코딩 방식">
<input type="..." />
<textarea></textarea>
<button></button>
</form>
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>카운트 증가</button>
<form>
<button type="submit">전송</button>
</form>
</>
)
}
export default App;
form 태그 안에 있는 내용들을 서버에 요청이 발생하는데
리액트 기반의 SPA 에서는 그런 현상이 일어나면 안된다.
e.preventDefault()
를 사용하자 !
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleSubmit = e => {
console.log("form 태그의 기본 동작이 처리되지 않도록 설정")
e.preventDefault();
console.log("서버로 요청이 전달되지 않도록 처리");
console.log("자바스크립트 코드를 이용해서 서버로 값을 전달하고, 전달받은 값을 사용하는 기능을 구현");
};
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>카운트 증가</button>
<form onSubmit={handleSubmit}>
<button type="submit">전송</button>
</form>
</>
)
}
export default App;
‼️
link, form, a 태그는 서버쪽으로 요청이 간다. 그래서 router를 이용해서 페이지 이동하자 !
💡
프로젝트를 하다가 키가 안 먹을 때 구글링해서 e.preventDefault(); 이걸 넣어서 해결한 적이 많은데 이런 이유인 줄 처음 알았다 ! wow