whatisthis?
220315 [TIL] Today I Learned 💯 React.js - (1) 본문
React.js
Day 03 React Hooks + WebPack
1 / React Hooks
리액트에서 추천하는 것 = Hooks의 사용.
Class Component를 자제하고, 함수형 프로그래밍을 위한 Hooks를 권고함.
클래스 컴포넌트 | class Button extends React.Component { // 생략 render () { return ( <button></button> ) } |
함수형 컴포넌트 | const Button = () => { // 생략 return <button></button> } |
원래도 함수형 컴포넌트가 있었는데,
그전에는 state나 ref 등을 클래스에서만 쓸 수 있었다.
💡 함수형에서도 state랑 ref를 쓸 수 있게 만든 것 = React Hooks
(+ useEffect)
useState()
React.useState() 사용.
대신, 클래스에서처럼 객체 형태로 한꺼번에 선언은 못하고,
state {
first: Math.ceil(Math.random() * 9),
second: Math.ceil(Math.random() * 9),
value: "",
result: "",
}
하나하나 쪼개서 선언해줘야함.
- 구조분해 할당 🔻
Destructuring assignment (구조분해 할당)
= 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// expected output: Array [30,40,50]
// 변수에 기본값을 할당하면, 분해한 값이 undefined일 때 그 값을 대신 사용합니다.
var a, b;
[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7
const [first, setFirst] = React.useState(Math.ceil(Math.random() * 9));
const [second, setSecond] = React.useState(Math.ceil(Math.random() * 9));
const [value, setValue] = React.useState('');
const [result, setResult] = React.useState('');
setState도 state 하나마다 하나씩 다 쪼개서 선언해줘야함.
(state-setState쌍)
단, class안에 state가 존재했듯이,
함수형 컴포넌트 내부에 존재해야함.
ref
클래스에서는 ref를 사용하기 위해서 아래와 같이 했었다.
input = null;
// 중략
<input ref={el => (this.input = el)} />
// 중략
this.input.focus();
DOM을 input에 대입하는 함수를 ref={}안에 넣어주고,
필요한 부분에 this.input.focus()를 해줬었다.
이제 Hooks에서는 React.useRef()를 이용해서 간단히 구현할 수 있다!
🔻
let inputRef = React.useRef(null);
// 중략
<input ref={inputRef} />
// 중략
inputRef.current.focus();
1. 변수 선언 - React.useRef(초기값);
2. ref속성에 inputRef 변수 대입.
3. 필요한 부분에 inputRef.current.focus() 해주기.
- 그냥 focus()말고 current.focus 해줘야한다!
지난번에 예제로 제작해본 구구단 게임을
클래스에서 함수형, 즉 Hooks로 변경해보자.
const GuGuDan = () => {
const [first, setFirst] = React.useState(Math.ceil(Math.random() * 9));
const [second, setSecond] = React.useState(Math.ceil(Math.random() * 9));
const [value, setValue] = React.useState("");
const [result, setResult] = React.useState("");
let inputRef = React.useRef(null);
const onFormSubmit = e => {
e.preventDefault();
if (parseInt(value) === first * second) {
setResult("⭕ 정답입니다");
setFirst(Math.ceil(Math.random() * 9));
setSecond(Math.ceil(Math.random() * 9));
setValue("");
inputRef.current.focus();
} else {
setResult("❌ 오답입니다");
setValue("");
inputRef.current.focus();
}
};
const onInputChange = e => {
setValue(e.target.value);
};
return (
<div>
<h1>💯구구단 게임💯</h1>
<div>
📃 {first} 곱하기 {second} 는?
</div>
<form onSubmit={onFormSubmit}>
<input
ref={inputRef}
type="number"
required
placeholder="정답 입력"
value={value}
onChange={onInputChange}
/>
<button type="submit">입력</button>
</form>
<div>{result}</div>
</div>
);
};
🔎 class 컴포넌트 코드와 비교해보자!
class GuGuDan extends React.Component {
state = {
first: Math.ceil(Math.random() * 9),
second: Math.ceil(Math.random() * 9),
value: "",
result: "",
line: "",
};
onFormSubmit = e => {
e.preventDefault();
if (parseInt(this.state.value) === this.state.first * this.state.second) {
this.setState(prevState => {
return {
result: "⭕정답입니다",
value: "",
first: Math.ceil(Math.random() * 9),
second: Math.ceil(Math.random() * 9),
line:
prevState.first +
"*" +
prevState.second +
"=" +
prevState.first * prevState.second,
};
});
this.input.focus();
} else {
this.setState(() => {
return { result: "❌틀렸습니다", value: "" };
});
this.input.focus();
}
};
onInputChange = e => {
this.setState({ value: e.target.value });
};
input;
render() {
return (
<div>
<h1>💯구구단 게임💯</h1>
<div>
📃 {this.state.first} 곱하기 {this.state.second} 는?
</div>
<form onSubmit={this.onFormSubmit}>
<input
ref={c => (this.input = c)}
type="number"
required
placeholder="정답 입력"
value={this.state.value}
onChange={this.onInputChange}
/>
<button type="submit">입력</button>
</form>
<span>LOG 🔎 {this.state.line}</span>
<div>{this.state.result}</div>
</div>
);
}
}
크게 달라진 점을 뽑아보면 다음과 같다.
1 / state를 객체로 나타내지 않고, 각 state를 분리해서 구조분해할당으로 선언했다.
>> const [ state, setState ] = React.useState('초기값'); 과 같이 선언.
2 / ref 변수를 React.useRef()로 선언하고, JSX안에선 ref={inputRef}와 같이 속성값에 변수를 대입했다.
>> focus()대신 inputRef.current.focus()로 포커스를 주었다.
함수형 setState
지난번에 이전 state를 사용할땐 setState를 함수형으로 적어줘야한다고 했었다.
🙋♂️함수형 setState를 언제 쓰는건지?
- setState안에 this.state(즉, 이전 state)가 들어가면
- 함수형으로 만들고, return { } 을 해주자!
this.setState((prevState) => { return { line: prevState.first + prevState.second }; });
Hooks에도 마찬가지로 예전 상태가 필요하다면 함수형으로 setState를 적어준다!
setResult("정답입니다!" + value);
🔻
setResult((prevResult) => {return "정답입니다! " + value});
Hooks의 리렌더링
함수형 컴포넌트 역시 state가 바뀔때마다 해당 함수가 재실행되기 때문에 (=리렌더링)
함수가 안에 많이 있다면 계속 재생성되어서 속도가 느려질수도 있다. >> 어쩔수 없는 부분임.
cf> class형에서는 클래스의 메서드로 따로 빼서 선언했기 때문에 (onFormSubmit, onInputChange)
render()안에 부분만 재실행됬었다.
하지만, Hooks에서는 어쩔 수 없이 함수 전체가 다시 실행된다.
+) React에서 ❌ 못쓰는 ❌ 속성
<HTML>태그의 속성 중에 class와 for은 js문법상 존재하는 것 (예약어)이므로
class 대신 className
for 대신 htmlFor 으로 적어주자.
2 / WebPack
React 할때 노드를 알아야한다?
>> javascript 실행기를 알아야한다.
웹팩을 돌리기 위한 javascript를 실행해야함.
step1
현재 프로젝트 루트에서 터미널 명령어
$ npm init 을 입력하자.
이런식으로 하나하나 입력해주고, 마지막으로 yes를 눌러주면
루트 폴더에 자동으로 📃package.json 이 생긴다!
step2
$ npm i react react-dom
react와 react-dom을 설치해줌.
이런식으로 node_modules 폴더가 생성된다.
>> 이 안에는 라이브러리들이 존재함.
package.json에는 라이브러리들의 dependencies들이 존재하는 것.
(실제 라이브러리들은 node_modules 에 존재함)
step 3
$ npm i -D webpack webpack-cli
🔻 🔎npm install 옵션 살펴보기
-D
package.json에서 dev-dependencies에 기록됨.
(개발할때만 필요한 것)
- 실제 서비스할때는 필요 없으므로!
- 실제 서비스할때 쓰이는 것들은 dependencies 로 기록됨.
📃 package.json
"dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2" }, "devDependencies": { "webpack": "^5.70.0", "webpack-cli": "^4.9.2" }
-g
글로벌 설치.
npm install name@latest // 가장 최신버전
npm install name@0.0.1 // 해당 버전
설치가 완료되었다면, 웹팩 설정 파일인
webpack.config.js 파일을 루트 폴더에 만들고, 저장해준다.
// 웹팩 설정 파일
module.exports = {
};
- 추후에 점점 추가할 것임.
루트 폴더에 client.jsx 파일을 생성한 후
react와 react-dom을 require()로 불러온다.
const React = require("react");
const ReactDom = require("react-dom");
const WordRelay = require("./WordRelay");
ReactDom.render(<WordRelay />, document.querySelector("#root"));
🔺 client.jsx
🔻 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>끝말잇기</title>
</head>
<body>
<div id="root"></div>
<script src="./dist/app.js"></script>
</body>
</html>
우리는 끝말잇기를 구현할 컴포넌트인 WordRelay.jsx를 따로 분리해서 생성하면 된다!
const React = require("react");
const { Component } = React;
class WordRelay extends Component {
state = {};
render() {}
}
module.exports = WordRelay;
❗❗ 주의
- 유지보수를 쉽게하기 위해 컴포넌트별로 파일로 분리하는 것이다.
- BUT! 매 파일마다 React를 require 로 불러오고,
- module.exports = 컴포넌트명; 을 꼭 해주자.
html 파일에는 script src="./dist/app.js" 하나만 연결되어 있다.
따라서, 모든 jsx 파일들을 app.js 하나로 합쳐줘야함.
>> 웹팩이 필요해! 🚀
다시 웹팩 설정 파일인
webpack.config.js를 수정해보자.
module.exports = {
name: "wordrelay-setting",
mode: "development", // 실서비스에선 'production'으로
devtool: "eval", // 빠르게
}
우선 기본적으로 name(셋팅 이름), mode(개발용/배포용) , devtool을 적어주고,
가장 중요한 entry: {} 와 output: {} 을 지정해주자.
entry = 입력
output = 출력
우리가 지금은 client.jsx와 WordRelay.jsx를 합쳐서
./dist/app.js로 만들어줘야 하므로
output은 ./dist/app.js 가 된다.
output: {
path: path.join(__dirname, "dist"), //__dirname은 현재폴더경로
filename: "app.js",
},
이런식으로 path: path.join()을 이용해서
__dirname(=현재 폴더 경로)에 'dist'를 합쳐주면 된다.
참고로, 위 코드를 작성하면 자동으로 아래와같이 require('path') 코드가 생성됨.
const path = require("path");
🔻
노드 문법임. 아래 문서를 참고하자.
- Path를 맞추기 위한 Path 모듈. (디렉토리 경로 작업)
entry에는 client.jsx와 WordRelay.jsx가 들어가면 된다.
근데 여기서 client.jsx가 WordRelay.jsx를 불러오므로 client.jsx만 적어주면 된다!
>> 확장자는 생략하기 위해, resolve라는 프로퍼티를 추가하자.
(나중에 가면 css 등 다양한 확장자명이 등장하니까)
resolve: {
extensions: [".js", ".jsx"],
},
entry: {
app: ["./client"],
},
>> 배열의 형태로 넣어준다.
** 지금까지의 코드
📃 webpack.config.js
const path = require("path");
module.exports = {
name: "wordrelay-setting",
mode: "development", // 실서비스에선 'production'으로
devtool: "eval", // 빠르게
resolve: {
extensions: [".js", ".jsx"],
},
entry: {
app: ["./client.jsx", "WordRealy.jsx"],
},
output: {
path: path.join(__dirname, "dist"), //__dirname은 현재폴더경로
filename: "app.js",
},
};
Practice - 끝말잇기 (Hooks 사용)
'WEB STUDY > REACT' 카테고리의 다른 글
220316 [TIL] Today I Learned 💯 React.js (0) | 2022.03.16 |
---|---|
220315 [TIL] Today I Learned 💯 React.js - (2) (0) | 2022.03.15 |
220314 [TIL] Today I Learned 💯 React.js (0) | 2022.03.14 |
220313 [TIL] Today I Learned 💯 React.js (0) | 2022.03.13 |
React. React JS - Cleanup function (0) | 2022.02.18 |