-> 2개 모두, export된 컴포넌트를 불러오기 위한 문법이라는 점은 같다.
-> 'module.export'로 내보낸 것을 불러올 때 사용!
-> 'export default'로 내보낸 것을 불러올 때 사용!
-> { } 형태는 구조 분해된 것인데, export 되는 자료형이 객체({ })나 배열([ ])이면 구조 분해할 수 있다.
--> 노드에서는 import 쓰면 에러!! 그러나, 바벨이 require로 바꿔준다. 하지만 웹팩(webpack.config.js)은 node로 돌아가므로, require을 써줘야 한다!!
⭕ 정리:
모듈별로 파일을 관리. 내보내거나 불러올 때, import나 export를 사용한다. import 와 require는 모두 export된 것을 불러오기 위함이다. import를 쓰려면, export default로 내보내야 함. require쓰려면, module.export 써야 함. 노드에서는 require를 사용함! import 사용해도 바벨이 바꿔주긴 해. 근데 웹 팩은 node로 돌아가므로 require 써야 함
(value랑 onChange는 항상 세트!!! -> 만약 같이 안할거면 defaultValue로)
-> map 함수 통해 구현
: 객체로 써줘도 되고, key는 고유해야 함, 반복되는 걸 배열로 만들어!
⭕ 정리:
상위 컴포넌트가 다른 컴포넌트들을 import 해주는 것임. props는 어떤 값을 컴포넌트에 넘겨줄 때 사용한다. 또한, 다른 파일에 있는 값을 사용할 파일에서 매개변수로 props 받아줌!!
import React, {Component, createRef} from 'react';
import Try from './Try';
function getNumbers(){
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(),
tries: [],
};
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 = () => {
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;
<바뀐 부분>
1.
2.
3.
-> class가 아닌 const로. state가 아닌, const로 useState 이용!
4.
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]);
-> setState 사용하지 않음
---> hooks는 setState, this 사용하지 않음, const로 화살표 함수로!!
렌더링:
: shouldcomponent update를 알아서 구현한 컴포넌트
-> class component 에만 쓸 수 있음
-> state가 바뀌었는지 자동으로 알아내줌 (바뀐 경우에만 렌더링), 옛날 객체나 배열 가지고 오지말고 새로 만들어라
-> 부모 컴포넌트가 리렌더링되면, 자식 컴포넌트도 리렌더링 됨
-> displayname: 원상태로 되돌려줌
----> 부모 리렌더링 시 자식도 리렌더링 되는 것을 막는 방법: class component-purecomponent/ 함수 컴포넌트: memo/ 직접: shouldcomponent update
(컴포넌트가 복잡해지면, purecomponent가 안되는 경우도 있음)
⭕ 정리:
'Shouldcomponentupdate' 와 'React.PureComponent' 와 memo 3개를 배웠다. 모두 바뀌어야하는 부분만 렌더링하도록 해주는 성능 최적화 API이다. 첫번째는 직접 조건을 걸어 렌더링을 시켜주는 것이다. 2번째는 class component에서 쓰이는 것으로, 부모에 변화가 있어 렌더링 되더라도, 자식에 직접적인 변화가 없으면 자식 컴포넌트에서는 렌더링이 되지 않는다. memo는 함수 컴포넌트에서 purecomponent와 같은 역할을 하는 것이다.
그런데, 리액트는 얕은비교를 하기 때문에, state를 업데이트할 때, 직접 업데이트를 하면 업데이트가 되지 않는다. 따라서 새로운 오브젝트를 새로 만들어, 그것을 업데이트 해주어야 한다. (위으 try와 같이)
<컴포넌트에 따른 레퍼런스를 만드는 방법>
⭕ 정리:
ref는 그 컴포넌트나 노드를 참조하기 위한 레퍼런스를 만드는 것이다. 그런데 어떤 컴포넌트냐에 따라 다르다. class component에서는 createRef를, 함수 component에서는 useRef를 사용해준다.