State → (값이 바뀔) 데이터가 저장되는 곳
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
let counter = 0;
function countUp() {
counter = counter + 1;
render();
}
function render() {
ReactDOM.render(<Container />, root);
}
const Container = () => (
<div>
<h3>Total clicks: {counter}</h3>
<button onClick={countUp}>Click me</button>
</div>
);
render();
</script>
</html>→ Total clicks: {counter} : {counter}로 변수를 가져올 수 있음
→ 초기의 렌더링만으로 끝내는 것이 아니라 리렌더링을 해줘야 변하는 UI 렌더링 가능
→ 현재 코드는 계속해서 모든 부분을 render 해주기 때문에 좋은 방식 X
→ vanillaJS와 달리 React.js는 UI에서 바뀐 부분만 업데이트 가능
(ex. button 업데이트 X, click 숫자만 업데이트)
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function App() {
const [counter, modifier] = React.useState(0);
return (
<div>
<h3>Total clicks: {counter}</h3>
<button>Click me</button>
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ 함수를 계속 불러줄 필요 없이 데이터를 보관하고 자동으로 리렌더링하는 최고의 방법
→ const data = React.useState(0); : [초기값(0), 함수] 2개가 담겨있는 배열, 함수의 역할
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function App() {
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(counter + 1);
};
return (
<div>
<h3>Total clicks: {counter}</h3>
<button onClick={onClick}>Click me</button>
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ React.useState : counter데이터를 숫자형 데이터로 전달, 데이터 값 바꿀 함수 같이 전달
(+ 함수로 바꾼 값 리렌더링)
위와 동일
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function App() {
const [counter, setCounter] = React.useState(0);
const onClick = () => {
// setCounter(counter + 1);
setCounter((current) => current + 1);
};
return (
<div>
<h3>Total clicks: {counter}</h3>
<button onClick={onClick}>Click me</button>
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ 1. setCounter(10); : 새 값으로 변경 (하지만 더 이상의 업데이트 X)
→ 2. setCounter(counter + 1); : 이전 값을 이용해서 현재 값을 계산 (계속 업데이트 O)
→ 3. setCounter((current) => current + 1); : Best Way / 확실히 현재 값 보장 (안전)
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function App() {
const [minutes, setMinutes] = React.useState(0);
const onChange = (event) => {
setMinutes(event.target.value);
};
return (
<div>
<h1>Super Converter</h1>
<label htmlFor="minutes">Minutes</label>
<input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} />
<h4>You want to convert {minutes}</h4>
<label htmlFor="hours">Hours</label>
<input id="hours" placeholder="Hours" type="number" />
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ NOT class YES className / NOT for YES htmlFor
→ 기본 input은 uncontrolled : value 값을 통제할 수 없음 → state 생성 필요
→ event.target.value : event를 사용하여 input 값 중 target의 value값 사용
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function App() {
const [minutes, setMinutes] = React.useState(0);
const onChange = (event) => {
setMinutes(event.target.value);
};
const reset = () => setMinutes(0);
return (
<div>
<h1>Super Converter</h1>
<div>
<label htmlFor="minutes">Minutes</label>
<input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} />
</div>
<div>
<label htmlFor="hours">Hours</label>
<input value={Math.round(minutes / 60)} id="hours" placeholder="Hours" type="number" disabled />
</div>
<button onClick={reset}>Reset</button>
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ Math.round() : 값 반올림해주는 함수
→ const reset = () => setMinutes(0); : 0으로 초기화
→ Hours : onChange 리스너가 없기 때문에 input 값 변경 불가
disabled 로 인해 입력 불가
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function App() {
const [amount, setAmount] = React.useState(0);
const [flipped, setFlipped] = React.useState(false);
const onChange = (event) => {
setAmount(event.target.value);
};
const reset = () => setAmount(0);
const onFlip = () => {
reset();
setFlipped((current) => !current);
};
return (
<div>
<h1>Super Converter</h1>
<div>
<label htmlFor="minutes">Minutes</label>
<input value={flipped ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={flipped} />
</div>
<div>
<label htmlFor="hours">Hours</label>
<input value={flipped ? amount : Math.round(amount / 60)} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled={!flipped} />
</div>
<button onClick={reset}>Reset</button>
<button onClick={onFlip}>Flip</button>
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ filp click : minutes 입력 hours 입력 불가 ↔ minutes 입력 불가 hours 입력
→ disabled={flipped === true}/disabled={!flipped} : disabled를 통해 flipped props 전달
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function App() {
const [amount, setAmount] = React.useState(0);
const [inverted, setInverted] = React.useState(false);
const onChange = (event) => {
setAmount(event.target.value);
};
const reset = () => setAmount(0);
const onInvert = () => {
reset();
setInverted((current) => !current);
};
return (
<div>
<h1>Super Converter</h1>
<div>
<label htmlFor="minutes">Minutes</label>
<input value={inverted ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={inverted} />
</div>
<div>
<label htmlFor="hours">Hours</label>
<input value={inverted ? amount : Math.round(amount / 60)} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled={!inverted} />
</div>
<button onClick={reset}>Reset</button>
<button onClick={onInvert}>{inverted ? "Turn back" : "Invert"}</button>
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ flipped를 inverted로 변경 및 버튼 {inverted ? "Turn back" : "Invert"} 추가
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function MinutesToHours() {
const [amount, setAmount] = React.useState(0);
const [inverted, setInverted] = React.useState(false);
const onChange = (event) => {
setAmount(event.target.value);
};
const reset = () => setAmount(0);
const onInvert = () => {
reset();
setInverted((current) => !current);
};
return (
<div>
<div>
<label htmlFor="minutes">Minutes</label>
<input value={inverted ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={inverted} />
</div>
<div>
<label htmlFor="hours">Hours</label>
<input value={inverted ? amount : Math.round(amount / 60)} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled={!inverted} />
</div>
<button onClick={reset}>Reset</button>
<button onClick={onInvert}>{inverted ? "Turn back" : "Invert"}</button>
</div>
);
}
function KmToMiles() {
return <h3>KM 2 M</h3>;
}
function App() {
const [index, setIndex] = React.useState("xx");
const onSelect = (event) => {
setIndex(event.target.value);
};
return (
<div>
<h1>Super Converter</h1>
<select value={index} onChange={onSelect}>
<option value="xx">Select your units</option>
<option value="0">Minutes & Hours</option>
<option value="1">Km & Miles</option>
</select>
<hr />
{index === "xx" ? "Please select your units" : null}
{index === "0" ? <MinutesToHours /> : null}
{index === "1" ? <KmToMiles /> : null}
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ select, value를 활용하여 메뉴바에서 선택하는 것으로 컴포넌트 불러오기
→ const [index, setIndex] = React.useState();
: useState에 아무 값도 지정해주지 않았기 때문에 처음에는 어느 화면도 X
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function Btn(props) {
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
}}
>
{props.banana}
</button>
);
}
function App() {
return (
<div>
<Btn banana="Save Changes" />
<Btn banana="Continue" />
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ 컴포넌트 : 어떤 JSX를 반환하는 함수
→ props : 버튼으로부터 전달받는 properties / 인자
→ Btn({banana:"Save Changes"}) = <Btn banana="Save Changes" />
→ 현재 이 코드에서 props는 버튼이 전달 받는 유일한, 첫번째 인자 → 값은 모두 가져옴
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function Btn({ text, big }) {
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize: big ? 18 : 16,
}}
>
{text}
</button>
);
}
function App() {
return (
<div>
<Btn text="Save Changes" big={true} />
<Btn text="Continue" big={false} />
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ props는 오브젝트이기 때문에 property를 오브젝트로부터 꺼내는 shortcut 사용
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function Btn({ text, changeValue }) {
return (
<button
onClick={changeValue}
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
}}
>
{text}
</button>
);
}
const MemorizedBtn = React.memo(Btn);
function App() {
const [value, setValue] = React.useState("Save Changes");
const changeValue = () => setValue("Revert Changes");
return (
<div>
<MemorizedBtn text={value} changeValue={changeValue} />
<MemorizedBtn text="Continue" />
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ props에 넣을 수 있는 것 : string, boolean, function 등
→ props는 일일이 추가해줘야 사용 가능
→ <Btn text="{value}" onClick={changeValue} /> : onClick은 이벤트리스너 X 하나의 props
이해를 위해 changeValue={changeValue}로 작성
→ React Memo : props가 변경되지 않을 때 컴포넌트를 리렌더링 하지 않고 싶을 때 사용
→ const MemorizedBtn = React.memo(Btn); : Btn을 Memo하여 변경된 것만 리렌더링
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function Btn({ text, fontSize = 16 }) {
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize,
}}
>
{text}
</button>
);
}
Btn.propTypes = {
text: PropTypes.string.isRequired,
fontSize: PropTypes.number,
};
function App() {
return (
<div>
<Btn text="Save Changes" fontSize={18} />
<Btn text="Continue" />
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>→ props에 타입을 지정해주어서 콘솔에서 오류 확인 가능 (다른 타입 작성하는 실수 방지)
위와 동일

→ create-react-app : React.js를 만드는 쉬운 방법
→ npx create-react-app 폴더명 : 리액트 앱 생성 명령어
→ npm start : 리액트 실행
→ package.json : 대부분의 내용이 담겨있음
→ 모든 폴더는 src 폴더 안에 위치
→ index.js : 가장 중요한 파일
→ 필요없는 파일 삭제 후 깨끗하게 시작 (App.js, index.js만 필요)
import Button from "./Button";
import styles from "./App.module.css";
function App() {
return (
<div>
<h1 className={styles.title}>Welcome back!!!</h1>
<Button text={"Continue"} />
</div>
);
}
export default App;.title {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
font-size: 18px;
}import PropTypes from "prop-types";
import styles from "./Button.module.css";
function Button({ text }) {
return <button className={styles.btn}>{text}</button>;
}
Button.propTypes = {
text: PropTypes.string.isRequired,
};
export default Button;.btn {
color: white;
background-color: tomato;
}import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);→ npm install prop-types : prop types를 사용하기 위해 설치하는 명령어
→ style을 모듈로 사용함으로써 같은 btn이라는 이름을 사용해도 다르게 적용 가능
(랜덤 방식으로 자동 생성 기능 존재)
import { useState } from "react";
function App() {
const [counter, setValue] = useState(0);
const onClick = () => setValue((prev) => prev + 1);
console.log("call an api");
return (
<div>
<h1>{counter}</h1>
<button onClick={onClick}>click me</button>
</div>
);
}
export default App;→ state가 변경될 때마다 리렌더링을 하게 되면 렌더링을 하고 싶지 않은 순간에도 진행
→ ex) API를 가져올 때 처음에만 렌더링하고 이후에 다시 정보를 가져올 필요 X
→ 첫번째 component render에서만 특정 코드들이 실행 (state 변화해도 코드 재실행 X)
→ useEffect 사용
import { useState, useEffect } from "react";
function App() {
const [counter, setValue] = useState(0);
const onClick = () => setValue((prev) => prev + 1);
console.log("i run all the time");
const iRunOnlyOnce = () => {
console.log("call the API");
};
useEffect(iRunOnlyOnce, []);
return (
<div>
<h1>{counter}</h1>
<button onClick={onClick}>click me</button>
</div>
);
}
export default App;→ useEffect : 두 개의 argument를 가지는 function
(1. 우리가 실행하고 싶은 코드 / 2. dependency(ies) : 리액트가 변화를 지켜볼 인자)
→ 코드가 딱 한번만 실행될 수 있도록 보호하는 역할
import { useState, useEffect } from "react";
function App() {
const [counter, setValue] = useState(0);
const onClick = () => setValue((prev) => prev + 1);
// console.log("i run all the time");
const [keyword, setKeyword] = useState("");
const onChange = (event) => setKeyword(event.target.value);
const iRunOnlyOnce = () => {
// console.log("call the API");
console.log("I run only once.");
};
useEffect(iRunOnlyOnce, []);
useEffect(() => {
// if (keyword !== "" && keyword.length > 5) {
// console.log("SEARCH FOR", keyword);
// }
console.log("I run when 'keyword' changes.");
}, [keyword]);
useEffect(() => {
console.log("I run when 'counter' changes.");
}, [counter]);
useEffect(() => {
console.log("I run when keyword & counter changes.");
}, [keyword, counter]);
return (
<div>
<input value={keyword} onChange={onChange} type="text" placeholder="Search here..." />
<h1>{counter}</h1>
<button onClick={onClick}>click me</button>
</div>
);
}
export default App;→ useEffect를 사용하지 않았을 때 검색어를 입력할 때마다 모든게 리렌더링됨
(혹은 click 버튼을 누를 때마다 입력되어 있는 검색어를 같이 리렌더링)
useEffect(() => {
console.log("SEARCH FOR", keyword);
}, **[keyword]**);
→ 키워드가 변화할 때만 코드를 실행(리렌더링)
→ [] 처럼 두번째 argument가 비어있을 때, 지켜볼게 없기 때문에 처음 한번만 실행
→ if (keyword !== "" && keyword.length > 5) : 키워드가 없거나 짧을 때 실행 X 조건
→ [keyword, counter] : array이기 때문에 2가지의 변화를 확인할 수도 O
Memo VS useEffect
→ Memo : props가 변경되지 않았을 때 리렌더링 X
→ useEffect : props가 변경될 때 함수 실행
혹은 컴포넌트의 시작, 끝에만 실행
→ state를 변화시킬 때 component를 재실행
→ useEffect는 react.js가 동작하는 관점에서 방어막
import { useState, useEffect } from "react";
function Hello() {
// function byFn() {
// console.log("bye :(");
// }
// function hiFn() {
// console.log("created :)");
// return byFn;
// }
useEffect(() => {
console.log("hi :)");
return () => console.log("bye :(");
}, []);
return <h1>Hello</h1>;
}
function App() {
const [showing, setShowing] = useState(false);
const onClick = () => setShowing((prev) => !prev);
return (
<div>
{showing ? <Hello /> : null}
<button onClick={onClick}>{showing ? "Hide" : "Show"}</button>
</div>
);
}
export default App;→ {showing ? <Hello /> : null} : 코드를 숨기는 것이 아니라 destroy 시키고 새로 생성
useEffect(() => {
console.log("created :)");
**return** () => console.log("destroyed :(");
}, []);
→ Cleanup function return : 함수인데 component가 destroy될 때 뭔가 할 수 있도록 해 줌
→ ex) 컴포넌트가 없어질 때 분석 결과를 보내고 싶으면 분석 API 사용 등
→ 자주 사용하지는 X