zeroCho[2/8] 숫자야구 끝 - rendering, shouldComponentUpdate, PureComponent, React.memo, React.createRef, props&state

목차
강의 소스: https://github.com/zerocho/react-webgame
[0/8] 구구단 - state, JSX, babel, Fragment, setState, ref
[1/8] 끝말잇기 - useState, class component vs function component, webpack, module, build
[1/8] 끝말잇기 - useState, class component vs function component, webpack, module, build
[2/8] 숫자야구. 3-1. import와 require 비교
[2/8] 숫자야구 - map, key, 컴포넌트분리, props, 메서드바인딩(this), 동작원리, 구조분해, 이벤트
[3/8] 반응속도체크 - createRoot, useEffect, useRef, 조건문, return 내부 for과 if 쓰기
[4/8] Rock Scissors Paper - LifeCycle, closure, 비동기함수, 고차함수, useEffect를 이용한 lifeCycle,
[5/8] Lotto - componentDidUpdate, useEffect, useMemo, useCallback
[6/8] 틱택토 - const [state, dispatch] = useReducer(reducer, initialState), action
강의목차
3-1. import와 require 비교
3-2. 리액트 반복문(map)
3-3. 리액트 반복문(key)
3-4. 컴포넌트 분리와 props
3-5. 주석과 메서드 바인딩
3-6. 숫자야구 만들기
3-7. Q&A
3-8. 숫자야구 Hooks로 전환하기
3-9. React Devtools
3-10. shouldComponentUpdate
3-11. PureComponent와 React.memo
3-12. React.createRef
3-13. props와 state 연결하기
3-8. 숫자야구 Hooks로 전환하기
깃 링크: https://github.com/Harimad/zeroReact/commit/4f7dcf0f2c409bfdfb29c40aa5a879563f121e26
Q. Input 에 값을 넣을때마다 리랜더링 되면서 answer 값이 바뀌는 현상?
Class 컴포넌트를 함수형 컴포넌트로 전환함.
함수형 컴포넌트로 전환했기 때문에 useState 값이 바뀌면 함수 전체가 리랜더링 된다.
그러면 getNumbers() 함수가 새로 호출되면서 answer값이 계속 바뀌는 문제가 발생한다.
-> 제로초曰: getNumbers()가 매번 실행되는 게 맞습니다.
이 문제는 나중에 useMemo를 써서 해결하고요.
다만 getNumbers()가 매번 실행되어도 state에는 영향을 주지 못합니다. 나중에 처리할 예정
3-9. React Devtools
3-10. shouldComponentUpdate
Q. 버튼을 클릭시에 onClick 함수의 콜백함수에서 setState메서드를 호출하는것 만으로도 리랜더링이 일어나는가?
-> 일어난다.
import React, { Component } from 'react'
class Test extends Component {
state = {
counter: 0,
}
onClick = () => {
// setState만 호출되어도 render부분이 재호출됨.
this.setState({})
}
render() {
console.log('렌더링', this.state)
return (
<div>
<button onClick={this.onClick}>클릭</button>
</div>
)
}
}
export default Test
실행결과: 클릭하면 재랜더링 됨

Q. 버튼을 클릭했을 때 state 값이 바뀐게 없다면 리랜더링이 일어나지 않도록 하려면?
-> shouldComponentUpdate메서드를 사용한다.
class Test extends Component {
state = {
counter: 0,
}
//어떨때 랜더링이 다시 되는지 설정해줘야함
shouldComponentUpdate(nextProps, nextState, nextContext) {
if (this.state.counter !== nextState.counter) {
return true //지금 counter와 미래의 counter가 다른게 true면 랜더링 해줘~
}
return false //여기서는 버튼을 눌러도 두 값이 같으므로 false 리턴 -> 클릭 해도 랜더링 일어나지 않음.
}
onClick = () => {
// setState만 호출되어도 render부분이 재호출됨.
this.setState({})
}
render() {
console.log('렌더링', this.state)
return (
<div>
<button onClick={this.onClick}>클릭</button>
</div>
)
}
}
실행결과

3-11. PureComponent와 React.memo
ㄱ.
PureComponent가 shouldComponentUpdate를 알아서 구현한 방법이다
shouldComponentUpdate(nextProps, nextState, nextContext) {}
purecomponent는 props가 바뀌었을 때만 리렌더링해주는 컴포넌트이다.
ㄴ. array를 새로 생성하고 거기에 값을 추가한다음 this.state에 참조값을 바로 집어넣으면?
-> 리액트는 값이 변경되었다고 판단을 못함. 리랜더링 일어나지 않음. 문제가 될 여지가 있다
ㄷ. 이전 state값을 복사하고 원하는 값을 넣어서 새로운 배열을 할당해주는 방식은?
-> 이런 방식을 사용해야 리액트가 state값이 변경되었다고 인식함. -> 리랜더링이 일어남
import React, { PureComponent } from 'react'
class Test extends PureComponent { // ㄱ
state = {
counter: 0,
string: 'hello',
number: 1,
boolean: true,
array: [],
object: {},
}
onClick = () => {
const array = this.state.array
array.push(1)
this.setState({ // ㄴ
array: array,
})
// this.setState({ // ㄷ
// array: [this.state.array, 1],
// })
}
render() {
console.log('렌더링', this.state)
return (
<div>
<button onClick={this.onClick}>클릭</button>
</div>
)
}
}
export default Test
ㄱ. 자식 컴포넌트에서 PureComponent를 쓰면?
-> props로 받아온 값의 변경유무를 체크해서 리랜더링을 해줌 -> 리랜더링 낭비 막아줌
import React, { PureComponent } from 'react'
class Try extends PureComponent { // ㄱ
render() {
const { tryInfo } = this.props
return (
<li>
<div>{tryInfo.try}</div>
<div>{tryInfo.result}</div>
</li>
)
}
}
// const Try = ({ tryInfo }) => {
// return (
// <>
// <li>
// <div>{tryInfo.try}</div>
// <div>{tryInfo.result}</div>
// </li>
// </>
// )
// }
export default Try
Hooks 에서는 React.memo 가 있다.
import React, { memo } from 'react'
const Try = memo(({ tryInfo }) => {
return (
<>
<li>
<div>{tryInfo.try}</div>
<div>{tryInfo.result}</div>
</li>
</>
)
})
export default Try
실행결과 : 리랜더링이 일어나지 않음. 왜?
부모 컴포넌트에서는 input 에 값을 넣어서 state가 변경되어 리랜더링이 일어나더라도
자식컴포넌트인 Try에서는 자식 state만 변경되면 리랜더링이 일어나도록
React.memo 나 PureComponent 를 설정했기 때문이다.
실행결과
결론
기본적으로 리액트는 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 리렌더링된다.
이 때 자식 컴포넌트를 PureComponent나 ShouldComponentUpdate로 만들면
자식 컴포넌트의 props가 바뀌었을때만 리렌더링한다.
props가 바뀌었을 때만 리렌더링해주는 컴포넌트인 PureComponent나 React.meom를 애용하자.
3-12. React.createRef
Class 컴포넌트 에서 Ref 사용하기 vs function 컴포넌트 에서 Ref 사용하기
1. Class 에서 Ref 사용하기
1. 함수선언 <- 추가적인 함수를 넣어 커스터마이징 가능
onInputRef = e => { this.inputRef = e }
2. 함수호출
render() {
return <input ref={this.onInputRef}/>
}
3. focus메서드 사용
this.inputRef.focus()
2. Hooks 에서 Ref 사용하기
1. React.useRef 가져오기
import React, { useRef } from 'react'
2. useRef써서 null로 초기화
const inputEl = useRef(null)
3. retrun 부분에서 태그에 useRef 담긴 변수 연결
<input ref={inputEl} />
4. focus메서드 사용할때 current 프로퍼티 붙이기
inputEl.current.focus()
3. Class 에서 Ref 를 Hooks 처럼 사용하기
1. import React, { Component, createRef } from 'react'
2. inputRef = createRef()
3. render() {
return <input ref={this.inputRef}/>
}
4. this.inputRef.current.focus()
결론: Hooks에서 useRef를 쓰면 편리하긴하다.
하지만 class 에서는 함수선언 부분에서 추가적인 함수작업이 가능하기 때문에 커스터마이징에 강점이 있다.
Q. useState와 useRef 차이 질문
-> useRef는 값이 바뀌어도 화면이 새로 그려지지 않고 useState는 새로 그려집니다. 끝
3-13. props와 state 연결하기
0. class 컴포넌트에서 render 안에 setState를 쓰면 안된다. 무한호출이 되어버린다.
render -> setState를 호출
setState -> render 호출
다시, render -> setState 호출
무한반복...
render(
return (
this.setState( { } )
)
)
자식 컴포넌트에서 props 받아오기 ( Class컴포넌트 vs Function컴포넌트)
1. Function컴포넌트
import React, { useState } from 'react'
const Try = ({ tryInfo }) => {
return (
<>
<li>
<div>{tryInfo.try}</div>
<div onClick={onClick}>{tryInfo.result}</div>
</li>
</>
)
}
export default Try
1-1. Function 컴포넌트에서 부모의 props 값을 받아서 활용하기
props 를 state로 만들어서 그 state를 바꾼다.
import React, { useState } from 'react'
const Try = ({ tryInfo }) => {
// 아래처럼자식이 부모의 state를 직접 바꾸면 안된다.
// tryInfo.try = 'hello'
// 직접 바꿀때는 state에 값을 넣어준다. -> 좋은 방법은 아님
// 자식이 부모 state를 바꾸면 부모 state에서 관리가 어려울 수 있다.
// 그러므로 아래처럼 부모값을 받아오고 새로운 state에 넣어줘야함.
const [result, setResult] = useState(tryInfo.result)
const onClick = () => {
setResult('1')
}
return (
<>
<li>
<div>{tryInfo.try}</div>
<div onClick={onClick}>{tryInfo.result}</div>
</li>
</>
)
}
export default Try
2. Class 컴포넌트
import React, { PureComponent } from 'react'
class Try extends PureComponent {
render() {
const { tryInfo } = this.props
return (
<li>
<div>{tryInfo.try}</div>
<div>{tryInfo.result}</div>
</li>
)
}
}
export default Try
2-1 Class 컴포넌트에서 props 를 state로 만들고 싶을땐
import React, { PureComponent } from 'react'
class Try extends PureComponent {
constructor(props) { // 방법 1. constructor 쓰기 -> 미세한 추가 동작 가능
super(props)
const { tryInfo } = this.props
this.state = {
result: tryInfo.result,
try: tryInfo.try,
}
}
render() {
const { tryInfo } = this.props
return (
<li>
<div>{tryInfo.result}</div>
<div>{tryInfo.try}</div>
</li>
)
}
}
shouldComponentUpdate 메서드에서 nextContext의 역할? 자세한건 추후에..
shouldComponentUpdate(nextProps, nextState, nextContext) {
//
}
A -> B -> C -> D -> E
A -> C 로 props 넘길때는 A -> B-> ... -> E 로 넘겨줘야함( B,C,D가 의도치 않은 랜더링 발생)
A -> E 로 바로 넘기고 싶을때 nextContext 사용함
이걸 응용한 것이 redux임