2021.04.30
js에서 주석 : //내용입력
React(jsx)에서 주석 : {/* 내용입력 */}
require
와 import
는 모듈 키워드이다.commonJS(require)
와 ES2015(import)
모듈 문법의 차이이다.require
만 지원하기 때문에 import
구문을 사용하려면 babel이 필요하다.require
는 NodeJs에서 사용되고 있는 CommonJS 키워드이다.
불러오기
var foo = require("foo");
var bar = requre("foo").bar;
내보내기
module.exports = foo;
exports.bar = bar;
모듈 정의하기
//모듈 전체를 export, module.exports = export default
module.exports = module;
// 함수나 변수를 직접 export가능
exports.moduleFunc = function() {};
module.exports = { bar: 'bar'}; // exports.bar = 'bar'; 같다.
import
는 ES2015에서 새롭게 도입된 키워드이다.
Babel
과 같은 ES6
코드를 변환(transpile)해주는 도구를 사용할 수 없는 경우에는 require
키워드를 사용해야 한다.
불러오기
import foo from "foo";
import { bar } from "foo";
내보내기
export default foo;
export { bar };
모듈 정의하기
//모듈 전체를 export, export default는 파일내에서 한 번만 사용가능하다.
const module = {};
export default module; // default로 export한 경우 불러올때 import module from '~~~';
// 함수나 변수를 직접 export가능, export는 여러번 가능하다.
export function moduleFunc() {};
export const bar = 'bar'; //그냥 export한 경우 불러올때 import {bar} from '~~~';
export default
와 module.exports
는 호환되지만 엄밀히 따지면 다르다.
exports되는 게 객체나 배열이면 구조 분해할 수 있다.
[].map(() => {
//식 입력
});
//ex 2배의 수 만들기
[1,2,3,4].map((num) => {
num * 2
});
//render() {return ()} 구문에 JSX문법으로 작성하기
//중괄호 {}를 이용한 JSX문법에 포함 시킬 수 있다.
const numbers = [1, 2, 3, 4, 5];
<ul>
{
numbers.map((number) => {
return (
<li>{number}</li>
);
})
}
</ul>
//화살표 함수를 사용하면 return 생략이 가능하다
//화살표 함수에서 ()소괄호는 return을 의미한다.
<ul>
{
numbers.map((number) => (
<li>{number}</li>
)
);
}
</ul>
//아예 소괄호까지 제거해도 return 생략 가능
// const a = (props) => console.log(props) 중괄호가 없는 것은 바로 return을 뜻 함
<ul>
{
numbers.map((number) =>
<li>{number}</li>
)
}
</ul>
key로 인덱스를 사용하는 것은 권장하지 않는다. 성능 저하나 컴포넌트의 state와 관련된 문제가 발생할 수 있다.
나중에 성능 최적화 시 문제가 될 수 있음
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
);
Props
: 속성을 나타내는 데이터이다.Props
객체 인자를 받은 후 React 엘리먼트를 반환한다 = 유효한 React 컴포넌트이다.Props
이다.props
는 부모한테서 받은 유산이다!!class NumberBaseball extends Component {
state = {
value: '',
};
onSubmit() {
console.log(this.state.value);
// this를 찾지 못한다 - 에러 발생
}
}
// 위 문제를 해결하기 위해서 메서드 바인딩이 필요하다.
class NumberBaseball extends Component {
//constructor를 작성해줘야 한다.
constructor(props) {
super(props);
state = {
value: '',
};
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit() {
console.log(this.state.value);
}
}
push
를 사용하지만 react에서는 push
를 사용하면 안된다.push
를 사용하지 않고 Spread 연산자를 사용해 새로운 배열을 만들어 준다.const array = [];
array.push(1);
//array = [1] 이 된다.
// array === array => true 기존 배열에 값만 추가된 것이므로 true가 반환된다.
const array2 = [...array, 2]
// 이때 array === array2 => false 다른 새로운 변수가 생성된 것이므로 false가 반환된다. 서로 다르기 떄문에 react가 감지 할 수 있다.
import React, { Component, createRef } from "react";
import Try from "./Try";
function getNumbers() {
// 겹치지 않는 랜덤한 숫자 4개를 뽑는 함수
const candidate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const array = [];
for (let i = 0; i < 4; i += 1) {
const chosen = candidate.splice(
Math.floor(Math.random() * (9 - i)),
1
)[0];
array.push(chosen);
}
return array;
}
class NumberBaseball extends Component {
state = {
result: "",
value: "",
answer: getNumbers(), // ex: [1,3,5,7]
tries: [], // push 쓰면 안 돼요
};
onSubmitForm = (e) => {
//비구조화 활당을 사용해서 간단하게 만들 수 있다.
const { value, tries, answer } = this.state;
e.preventDefault();
if (value === answer.join("")) {
this.setState((prevState) => {
return {
result: "홈런!",
tries: [
...prevState.tries,
{ try: value, result: "홈런!" },
],
};
});
alert("게임을 다시 시작합니다!");
this.setState({
value: "",
answer: getNumbers(),
tries: [],
});
this.inputRef.current.focus();
} else {
// 답 틀렸으면
const answerArray = value.split("").map((v) => parseInt(v));
let strike = 0;
let ball = 0;
if (tries.length >= 9) {
// 10번 이상 틀렸을 때
this.setState({
result: `10번 모두 시도했습니다. 답은 ${answer.join(
","
)}였습니다!`,
});
alert("게임을 다시 시작합니다!");
this.setState({
value: "",
answer: getNumbers(),
tries: [],
});
this.inputRef.current.focus();
} else {
for (let i = 0; i < 4; i += 1) {
if (answerArray[i] === answer[i]) {
strike += 1;
} else if (answer.includes(answerArray[i])) {
ball += 1;
}
}
this.setState((prevState) => {
return {
tries: [
...prevState.tries,
{
try: value,
result: `${strike} 스트라이크, ${ball} 볼입니다`,
},
],
value: "",
};
});
this.inputRef.current.focus();
}
}
};
onChangeInput = (e) => {
console.log(this.state.answer);
this.setState({
value: e.target.value,
});
};
inputRef = createRef(); // this.inputRef
render() {
const { result, value, tries } = this.state;
return (
<>
<h1>{result}</h1>
<form onSubmit={this.onSubmitForm}>
<input
ref={this.inputRef}
maxLength={4}
value={value}
onChange={this.onChangeInput}
/>
</form>
<div>시도: {tries.length}</div>
<ul>
{tries.map((v, i) => {
return <Try key={`${i + 1}차 시도 :`} tryInfo={v} />;
})}
</ul>
</>
);
}
}
export default NumberBaseball;
import React, { useRef, useState, useCallback } from "react";
import Try from "./Try";
const getNumbers = () => {
//겹치지 않는 랜덤한 숫자 4개를 뽑는 함수
const candidates = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const array = [];
for (let i = 0; i < 4; i += 1) {
const chosen = candidates.splice(
Math.floor(Math.random() * (9 - i)),
1
)[0];
array.push(chosen);
}
return array;
};
const NumberBaseball = () => {
const [answer, setAnswer] = useState(getNumbers());
const [value, setValue] = useState("");
const [result, setResult] = useState("");
const [tries, setTries] = useState([]);
const inputEl = useRef(null);
const onSubmitForm = useCallback(
(e) => {
e.preventDefault();
if (value === answer.join("")) {
setTries((t) => [
...t,
{
try: value,
result: "홈런!",
},
]);
setResult("홈런!");
alert("게임을 다시 실행합니다.");
setValue("");
setAnswer(getNumbers());
setTries([]);
inputEl.current.focus();
} else {
const answerArray = value.split("").map((v) => parseInt(v));
let strike = 0;
let ball = 0;
if (tries.length >= 9) {
setResult(
`10번 모두 시도했습니다! 답은 ${answer.join(
","
)}였습니다!`
); // state set은 비동기
alert("게임을 다시 시작합니다.");
setValue("");
setAnswer(getNumbers());
setTries([]);
inputEl.current.focus();
} else {
console.log("답은", answer.join(""));
for (let i = 0; i < 4; i += 1) {
if (answerArray[i] === answer[i]) {
console.log("strike", answerArray[i], answer[i]);
strike += 1;
} else if (answer.includes(answerArray[i])) {
console.log(
"ball",
answerArray[i],
answer.indexOf(answerArray[i])
);
ball += 1;
}
}
setTries((t) => [
...t,
{
try: value,
result: `${strike} 스트라이크, ${ball} 볼입니다.`,
},
]);
setValue("");
inputEl.current.focus();
}
}
},
[value, answer]
);
const onChangeInput = useCallback((e) => setValue(e.target.value), []);
return (
<>
<h1>{result}</h1>
<form onSubmit={onSubmitForm}>
<input
ref={inputEl}
maxLength={4}
value={value}
onChange={onChangeInput}
/>
<button>입력!</button>
</form>
<div>시도: {tries.length}</div>
<ul>
{tries.map((v, i) => (
<Try key={`${i + 1}차 시도 : ${v.try}`} tryInfo={v} />
))}
</ul>
</>
);
};
export default NumberBaseball;
import React, { Component } from "react";
class Try extends Component {
render() {
const { tryInfo } = this.props;
return (
<li>
<div>{tryInfo.try}</div>
<div>{tryInfo.result}</div>
</li>
);
}
}
export default Try;
import React, { memo } from "react";
const Try = memo(({ tryInfo }) => {
return (
<li>
<div>{tryInfo.try}</div>
<div>{tryInfo.result}</div>
</li>
);
});
export default Try;
React Devtools
확장 프로그램을 설치해 준다.setState()
만 호출해도 렌더링이 된다. 이럴때는 shouldComponentUpdate()
를 사용한다.// render()메서드 처럼 react에서 지원하는 메서드이다.
shouldComponentUpdate(nextProps, nextState, nextContext) {
//어떤 경우에 렌더링을 할 지를 직접 작성해 준다.
//예를들어
if(this.state !== nextState) {//현재 state와 미래 state가 다르면
return true; //렌더링해준다.
}
return false;
}
shouldComponentUpdate()
메서드의 사용이 복잡한 경우Component
대신 PureComponent
를 사용한다.import React, {PureComponent} from 'react';
class Test extends PureComponent { }
PureComponent
: shouldComponentUpdate()
를 알아서 구현한 Component이다.memo()
를 사용한다.memo()
로 감싸주면 된다.import React, {memo} from 'react';
const Test = memo( () => {} );
React.createRef
사용import React, { Component, createRef } from
class Test extends Component {
onSubmit = () => {
this.inputRef.current.focus();
}
inputRef = createRef();
render() {
return (
<>
<form onSubmit={this.onSubmit}>
<input ref={this.inputRef} />
</form>
</>
)
}
}