import , require
const React = require('react');
import React from 'react';
module.exports = NumberBaseball
export default NumberBaseball;
// export default 차이
export const hello = 'hello'; // import {hello} from './[파일경로]'
export default NumberBaseball; // import NumberBaseball from './[파일경로]'
// default 는 한번만 사용가능
Key 에 index 를 써도 되는 경우는
요소가 추가만 되는 경우이다.
복잡하고 긴 부분 컴포넌트로 따로 빼기
전체코드
import React, { Component } from "react";
function getNumbers() {}
class NumberBaseball extends Component {
state = {
result: "",
value: "",
answer: getNumbers(),
tries: [],
};
onSubmitForm = () => {};
onChangeInput = () => {};
fruits = [
{ fruit: "사과", taste: "맛있다" },
{ fruit: "감", taste: "떫다" },
{ fruit: "귤", taste: "새콤달콤하다" },
{ fruit: "배", taste: "맛있다" },
{ fruit: "딸기", taste: "맛있다" },
{ fruit: "레몬", taste: "시다" },
{ fruit: "오렌지", taste: "맛있다" },
];
render() {
return (
<>
<h1>{this.state.result}</h1>
<form onSubmit={this.onSubmitForm}>
<input
maxLength={4}
value={this.state.value}
onChange={this.onChangeInput}
/>
</form>
<div>시도: {this.state.tries.length}</div>
<ul>
{this.fruits.map((v, i) => {
return (
<li key={v.fruit + v.taste}>
<b>{v.fruit}</b> - {i}
</li>
);
})}
</ul>
</>
);
}
}
export default NumberBaseball; // import NumberBaseball;
이 부분을 컴포넌트를 사용해서 가독성을 높여보자.
<ul>
{this.fruits.map((v, i) => {
return (
<li key={v.fruit + v.taste}>
<b>{v.fruit}</b> - {i}
</li>
);
})}
</ul>
Try.jsx 파일
import React, { Component } from "react";
export default class Try extends Component {
render() {
return (
<li key={v.fruit + v.taste}>
<b>{v.fruit}</b> - {i}
</li>
);
}
}
위와 같이 컴포넌트로 바꾸어서 Try 하나만 import 해주면 되기 때문에 코드가 깔끔해진다고 볼 수 있다.
정상적으로 작동하기 위해서는 props를 넘겨주어야한다.
<ul>
{this.fruits.map((v, i) => {
return (
<Try />
);
})}
</ul>
컴포넌트를 따로 빼는 이유
- 가독성
- 재사용성
- 성능 최적화에 유리
class 컴포넌트에서 props 넘겨주는 방법
넘겨줄때는 함수형과 똑같다. 받는게 조금 다르다.
<ul>
{this.fruits.map((v, i) => {
return <Try value={v} index={i}></Try>;
})}
</ul>
받을 때는 this.props.value , this.props.index 를 사용해서 받는다.
import React, { Component } from "react";
export default class Try extends Component {
render() {
return (
<li key={this.props.value.fruit + this.props.value.taste}>
<b>{this.props.value.fruit}</b> - {this.props.index}
</li>
);
}
}
클래스 컴포넌트에서 this.state 안쓰는 방법
구조분해 할당을 사용한다.
const { value, tries, answer } = this.state;
lazy init
아래 코드를 보면 useState( )안에 함수를 넣어주는 코드를 볼 수 있다.
이러한 경우에 getNumbers 로 넣어주었을때와 getNumbers( )로 넣어주었을 때의 차이가 존재한다.
- getNumbers 로 넣어주게 되면 처음 렌더링될때에만 getNumbers 함수의 리턴값이 answer 에 할당된다.
- getNumbers( )로 넣어주게 되면 매번 리렌더링 될때마다 getNumbers 함수가 호출된다.
따라서 만약 복잡한 연산의 함수라면 lazy init 을 사용하는 것이 더 성능에 좋을 것이다.
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); // lazy init
// const [answer, setAnswer] = useState(getNumbers());
}
그러나 setAnswer 에서는 getNumber() 로 사용하는 것이 좋다.
왜냐하면 setAnswer 가 호출될때마다 내부의 getNumbers 도 새로운 리턴값을 넘겨주어야하기 때문이다.
const onSubmitForm = useCallback(
(e) => {
e.preventDefault();
if (value === answer.join("")) {
...
setAnswer(getNumbers());
...
} else {
...
},
[value, answer]
);
shouldComponentUpdate
다음 같은 코드가 있을때, 버튼을 클릭하면 리렌더가 될까?
import React, { Component } from "react";
class Test extends Component {
state = {
counter: 0,
};
onClick = () => {
this.setState({});
};
render() {
console.log("렌더링", this.state);
return (
<div>
<button onClick={this.onClick}>클릭</button>
</div>
);
}
}
export default Test;
다음처럼 리렌더가 되는 모습을 알 수 있다.
여기서 주의해야할 점은 state 가 바뀌지 않고 setState 만 호출 되었음에도 리렌더가 발생한다는 것이다.
이럴때 shouldComponentUpdate 를 사용해서 어떤경우에 리렌더가 발생해야하는지 개발자가 정해줄 수 있다.
다음 코드를 해석하면 이전 counter 와 바뀐 counter 가 달라야지만 return true 가 되어 리렌더가 발생한다.
shouldComponentUpdate(nextProps, nextState, nextContext) {
if (this.state.counter !== nextState.counter) {
return true;
}
return false;
}
pureComponent
shouldComponentUpdate가 개발자의 의해 컨트롤 되야한다면 PureComponent는 자동으로 state 나 props 의 변화를 감지하여 리렌더발생을 컨트롤 해준다. 즉, 변화가 없다면 리렌더가 발생하지 않고 변화가 생긴다면 리렌더가 발생한다는 뜻이다.
아래 영상을 보면 그냥 Component 를 사용했을때와 PureComponent 를 사용했을때의 차이를 알 수 있다.
지금까지 class 컴포넌트에서 리렌더를 다루는 방법을 조금 배웠는데 함수형 컴포넌트에서는 어떻게 해야할까?
memo
자식 컴포넌트가 리렌더링 되는 조건은 다음과 같다.
- state 가 변경될때
- props 가 변경될때
- 부모 컴포넌트가 리렌더링 되었을때
여기서 memo는 부모 컴포넌트가 리렌더링 되었을때 자식컴포넌트가 리렌더링 되는 것을 막아준다.
memo 사용 X
memo 사용
자식 렌더에서 차이가 나는 것을 알 수 있다.
전체 코드는 다음과 같고 또한 한번더 상기해야할 부분은
Try.displayName = 'Try" 이다. 이 코드의 동작은 주석으로 달아놓았다.
import React, { memo } from "react";
const Try = memo(({ tryInfo }) => {
return (
<li>
<div>{tryInfo.try}</div>
<div>{tryInfo.result}</div>
</li>
);
});
Try.displayName = "Try"; // react devtools 에서 이름이 이상해지는 것을 수정하는 코드.
export default Try;
createRef
createRef 를 사용하면 class 컴포넌트에서도 함수형 컴포넌트의 useRef 와 유사한 코드로 ref 를 사용할 수 있게 된다.
다음과 같은 식으로 말이다.
inputRef = createRef(); // this.inputRef
this.inputRef.current.focus(); // ref 사용
<form onSubmit={this.onSubmitForm}>
<input
ref={this.inputRef}
maxLength={4}
value={value}
onChange={this.onChangeInput}
/>
</form>
props 를 수정해야할 경우?
부모에게서 넘겨져온 props 를 수정해야할 경우가 있는데 이런 경우에는 자식 컴포넌트에서 useState로 받아놓고 setState 를 사용하여 바꾸는 것이 좋다.
import React, { memo, useState } from "react";
const Try = memo(({ tryInfo }) => {
const [result, setResult] = useState(tryInfo.result);
const onClick = () => {
setResult("1");
};
return (
<li>
<div>{tryInfo.try}</div>
<div onClick={onClick}>{tryInfo.result}</div>
</li>
);
});
Try.displayName = "Try"; // react devtools 에서 이름이 이상해지는 것을 수정하는 코드.
export default Try;
다음과 같이 말이다.
const [result, setResult] = useState(tryInfo.result);
const onClick = () => {
setResult("1");
};
'공부기록 > 웹 개발' 카테고리의 다른 글
[웹 게임을 만들며 배우는 React] 5장 (0) | 2022.08.29 |
---|---|
[웹 게임을 만들며 배우는 React] 4장 (0) | 2022.08.26 |
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. (0) | 2022.08.25 |
[웹 게임을 만들며 배우는 React] 2장 (0) | 2022.08.25 |
[웹 게임을 만들며 배우는 React] 1장 (0) | 2022.08.24 |