whatisthis?

220316 [TIL] Today I Learned 💯 React.js 본문

WEB STUDY/REACT

220316 [TIL] Today I Learned 💯 React.js

thisisyjin 2022. 3. 16. 17:04

React.js

 

 

 

컴포넌트

 

- 기존 웹 프레임워크 = MVC 방식

(Model, View, Controller)

 

정보담당 Model

화면담당 View

구동담당 Controller

 

- 코드관리는 효율적

- 각 요소의 의존성이 높아 재활용 어려움

 

 

 

 

💡 import 문에서 파일 이름 확장자가 생략된 이유

 

- create-react-app의 

웹팩 module resolution 기능 때문.

 

- 확장자가 파일 이름에 있는 파일 먼저 임포트

- 확장자가 파일 이름에 없는 경우 - extentions에 정의된 확장자 목록을 보고 확인함.

- 지정 경로에 없다면 - 같은 이름의 폴더가 있는지 체크 - 같은 이름 폴더가 있다면 index 파일 검사

 

 

 

 

- 컴포넌트 구성요소

 

property

 

상위 -> 하위 컴포넌트로 전달되는 읽기 전용 데이터.

- 속성(Attribute) 형태로 전달됨.

 

- render 함수내에서 참조할 수 있음. (this.props.name 이런식)

- 단방향으로 데이터가 흐름. (상위컴포넌트에서 하위 컴포넌트로)

 

- 프로퍼티의 자료형을 미리 선언하는것이 좋음. >> prop-types 사용

import PropTypes from 'prop-types'

class PropsComponent extends React.Component {

render() {
   return( 
      <div> {this.props.name} </div>
      );
   }
}  

// 자료형 선언

PropsComponent.propTpyes = {
   name: PropTypes.string,
};

export default PropsComponent;

 

 

🙋‍♂️ TIP 1

 

프로퍼티 목록 만들기 (지역변수로 재정의)

class Child extends React.Component {
    render() {
        const {prop1, prop2, prop3} = this.props;

        return (
            <div>
                <span>{prop1}</span>
                <span>{prop2}</span>
                <span>{prop3}</span>
            </div>

        )
    }
}

 

+) Hooks로 할땐 (함수형) - props 대신 객체 안 값을 매개변수에 적어서 했었다.

const Child = ({ prop1, prop2, prop3 }) => {
  return (
    <div>
      <span>{prop1}</span>
      <span>{prop2}</span>
      <span>{prop3}</span>
    </div>
  );
};

 

🙋‍♂️ TIP 2

 

실무에서는 객체를 전달할 때 변수에 객체를 담아 전달함. { } 안에.

 

예>

class App extends React.Component {
   render() {
      return (
          <Child
             prop1 = {true}
             prop2 = {[1,2,3]}
             prop3 = {{name: 'yjin', age:23}}
           />
       )
    }
}

 

 

 

🙋‍♂️ TIP 3

 

- 불리언 값을 갖는 프로퍼티는 

 

<Child boolValue /> 

이런식으로 이름만 선언해도 true 전달

 

<Child />

이름을 생략하면 false 전달

 

** undefined와 false는 조건문에서 동일 취급하므로.

 

 

❕ 보통 boolean값은 삼항연산자의 조건으로 이용한다.

const message = this.props.prop1 ? '안녕하세요' : '누구세요';

return (
	<div> {message} </div>
);

 

___

 

import PropTypes from 'prop-type';

// Child 생략

Child.propTypes = {
	objValue : PropTypes.shape({
       name: PropTypes.string,
       age: PropTypes.number,
    }),
    
    requiredString : propTypes.string.isRequired,
}

 

- 필수 프로퍼티 사용
PropTypess.string.isRequired 와 같이
.isRequired를 뒤에 붙여주면 됨.


- 객체 프로퍼티 (자료형) 정의
PropTypes.shape() 이용.

 

 

- 프로퍼티 기본값 설정

 

Child.defaultProps()이용.

Child.propTypes = {
  prop1: PropTypes.bool,
  prop2: PropTypes.bool,
};

Child.defaultProps = {
    prop1 : false,
}

둘다 bool값이지만, 둘다 값을 지정하지 않았으므로 undefined가 되어야 할텐데

prop1은 디폴트값을 false로 했으므로 false가 된다.

 

 

 

- 자식 노드 존재시

 

🔻 App.jsx

import React from 'react';
import Child from

class App extends React.Component {
   render() {
      return (
         <div>
            <Child>
               <div><span>자식노드</span></div>
            </Child>
         </div>
      )
   }
}

export defalt App;

 

이런식으로 <Child />말고

<Child></Child> 사이 자식노드를 넣을수도 있다.

 

이렇게 자식노드로 들어간 노드는 this.props.childeren과 같은 방법으로 받을 수 있다.

 

 

🔻  Child.jsx

import React from "react";
import PropTypes from "prop-type";

class Child extends React.Component {
  render() {
    return <div>{this.props.children}</div>;
  }
}

Child.propTypes = {
  children: PropTypes.node,
};

export default Child;

 

 

 

 

state

 

컴포넌트의 상태를 저장하고 변경할 수 있는 데이터.

 

- 값을 바꿔야 하는 경우.

cf> 프로퍼티 : 컴포넌트 내부에서 값을 바꿀 수 없음. (읽기전용)

 

- 값을 저장하거나 변경할 수 있는 객체.

- 보통 클릭, 입력 등의 이벤트(event)와 함께 사용됨.

 

❗  state 사용시 주의점

 

1 / 생성자(constructor)에서 반드시 초기화.

- 반드시 초기화해야함.

- 마땅한 빈 값이 없으면 this.state에 빈 객체 { } 라도 넣어줌.

 

2 / state를 변경하려면 오직 setState()로만.

🙋‍♂️ state를 왜 직접 변경하면 안되는가?

render() 함수로 화면을 그려주는 시점은 리액트 엔진이 결정하기 때문.
state를 직접 변경하면 render()함수는 새로 호출되지 않음.
setState()로 state가 변경되면, 리액트 엔진이 자동으로 render()함수를 호출 - 리렌더링.

 

 

3 / setState()는 비동기로 처리되며, setState() 이후로 연결된 함수들이 실행 완료된 시점에 렌더링함.

 

 

4 / setState()의 인자로 함수 전달시 - 즉, 함수형 setState

>> 이전 state 값을 읽을 수 있음.

 

- 이전 state 값을 불러오려면

🔻 이런식으로 이전 state를 변수로 저장해서 불러와야하는데,

const { formData } = this.state;

this.setState ( {
   formData : formData + 'hello'
});

 

🔻 함수형 setState는 아래와 같이 이전 state를 매개변수로 해서 받아올 수 있다.

handleData(data) {
   this.setState( prevState => ({
       formData: prevState + 'hello',
       });
}

 

 

 

 

❕  if - 강제로 화면을 출력해주려면? 

forceUpdate() 함수를 이용.

 

handleData(data) {
   // state 직접 변경
   this.formData = this.formData + 'hello';
   this.forceUpdate();
}

 

forceUpdate()

 

- 컴포넌트 내장 함수로, 강제로 화면을 새로고침 해준다. 

 

 

 

💯 practice - 카운터 컴포넌트

class Counter extends React.Component {
  constructor(props) {
    super(props);
    // state 정의
    this.state = {
      count: 0,
    };
    this.increaseCount = this.increaseCount.bind(this);
  }

  increaseCount() {
    const count = this.state.count;
    this.setState(count => {count: count + 1})
  }

  render() {
    return (
      <div>
        <span>카운트: {this.state.count}</span>
        <button onClick={this.increaseCount}>카운트 증가</button>
      </div>
    );
  }
}
this.setState(count => {count: count + 1})

 


 

컴포넌트의 생명 주기

 

컴포넌트의 생성부터 소멸까지의 과정을 컴포넌트의 생명주기(=LifeCycle)이라 한다.

컴포넌트는 생명주기마다 함수를 가지고 있고, 이 함수들을 이용해 특성 시점에 원하는 동작을 하게 할 수 있다.

 

 

생명주기 함수

총 8종의 함수가 있다.

 

  1. constructor
  2. getDerivedStateFormProp
  3. render
  4. componentDidMount
  5. shouldComponnetUpdate
  6. getSnapshotBeforeUpdate
  7. componentDidUpdate
  8. componentWillUnmount

 

컴포넌트 생성
🔻
생성 완료
constructor
getDerivedStateFormProp
render
componentDidMount
생성 완료
🔻
갱신 완료

getDerivedStateFormProp
shouldComponnetUpdate
render
getSnapshotBeforeUpdate
componentDidUpdate
** shouldComponentUpdate()가
true면 이후 과정 진행

갱신 완료
🔻
소멸 완료
componentWillUnmount

 

 

1 / constructor(props)

 

맨 처음 생성될 때 한번만 호출. 상태(state)를 선언할 때 사용.

constructor 함수를 정의할 때는 항상 super(props) 를 맨 위에 호출.

 

 

2 / render()

 

데이터가 변경되어 새 화면을 그려야 할 때 자동으로 호출됨.

- render 함수가 return()하는 JSX를 화면에 렌더링해줌.

 

 

3 / static getDerivedStateFormProps(props, state)

 

getDerivedStateFormProps() 함수는 정적 함수이다.

>> this.props나 this.state로 접근 못함.

- 인자로 전달된 props, state를 사용해야 함.

 

- 상위 컴포넌트에서 받은 props로 state값을 연동할 때 주로 사용.

- return값으로 setState 해줌.

 

 

4 / componentDidMount()

 

render() 함수가 JSX를 화면에 렌더링 한 이후에 호출되는 함수.

- 컴포넌트가 화면에 모두 표현된 이후에 해야하는 작업들을 여기서 하면 됨.

 

 

5 / shouldComponentUpdate(nextProps, nextState) 

 

프로퍼티를 변경하거나 setState()를 호출하여 state를 변경하면

다시 렌더링해야하는지 판단하는 함수.

 

즉, 리렌더링 할지 말지 판단. 화면 변경을 위한 검증 작업.

데이터 변화를 비교하는 작업 포함됨. (성능에 영향 多)

 

forceUpdate()를 하면
shouldComponentUpdate()는 호출되지 않는다.

 

 

6 / getSnapShotBeforeUpdate(prevProps, prevState) 

 

컴포넌트의 변경된 내용이 가상 화면에 완성된 이후 호출됨.

- 컴포넌트가 화면에 실제로 출력되기 전에 호출됨.

- DOM 정보에 접근할 때 사용됨.

 

 

 

7 / componentDidUpdate(prevProp, prevState, snapshot) 

 

- 컴포넌트가 화면에 실제로 출력된 이후 호출되는 함수 

- props, state, snapshot을 인자로 전달받음.

- snapshot은 getSnapShotBeforeUpdate() 에서 리턴한 값.

- 스크롤 위치를 옮기거나 커서 이동 등의 DOM 정보를 변경할 때 사용.

 

🙋‍♀️ componentDidMount() vs. componentDidUpdate()

  • the DOM is available (initial render) - componentDidMount 
  • properties got changed - componentDidUpdate

 

8 / componentWillUnmount()

 

- 컴포넌트가 소멸되기 직전에 호출되는 함수

- 보통 컴포넌트에서 감시하고 있는 작업해제시 필요

예> setInterval()을 clearInterval() 로 해제시

 

 

 

 

<과정>

 

1️⃣ 생성 과정의 생명주기 함수 

 

1. constructor

2. getDerivedStateFormProps

3. render

4. componentDidMount

 

 

- componentDidMount()에 setState()로 state 변경.

 

2️⃣ 변경 과정의 생명주기 함수

 

1. getDerivedStateFormProps

2. shouldComponentUpdate

true가 나오면 🔻

3. render

4. getSnapshotBeforeUpdate

5. componentDidUpdate

 

 

 

만약 shouldComponentUpdate가 false여도 렌더링 하려면 - forceUpdate()를 하면 된다.

>>  componentDidMount() 안에 setState() 대신 forceUpdate()를 해주자,

 

 

 

3️⃣ 소멸 과정의 생명주기 함수

 

- 컴포넌트가 화면에서 생략되면 시작됨.

return null하면 - 소멸.

 

- 소멸 직전 componentWillUnmount()가 실행됨.

 


 

🎲 예제 - 카운터 프로그램

 

- 만약 다른곳에서 (useState가 아닌 곳에서) state가 변경될 수 있으므로

getDerivedStateFromProps()로 프로퍼티가 변경되어도 호출되도록 하자.

 

 

 

 

1️⃣ Counter.jsx

import React from "react";

class Counter extends React.Component {
  state = {
    count: this.props.count,
  };

  increaseCount = () => {
    this.setState(({ count }) => ({
      count: count + 1,
    }));
  };

  render() {
    return (
      <div>
        현재 카운트 : {this.state.count}
        <button onClick={this.increaseCount}>카운트 증가</button>
      </div>
    );
  }
}

export default Counter;

 

2️⃣ NewCounter.jsx

(getDerivedStateFromProps 사용시)

import React from "react";

class NewCounter extends React.Component {
  state = {};

  static getDerivedStateFromProps(props, state) {
    const { count } = props; // props.count를 count로 저장
    return {
      count,
      newCount: count === state.count ? state.newCount : count,
    };
  }

  increaseCount = () => {
    this.setState(({ newCount }) => ({
      newCount: newCount + 1,
    }));
  };

  render() {
    return (
      <div>
        현재 카운트: {this.state.newCount}
        <button onClick={this.increaseCount}>카운트 증가</button>
      </div>
    );
  }
}

export default NewCounter;

 

 

 

 두 컴포넌트를 비교해보기 위해 App 컴포넌트에 포함시켜 출력하자.

 

🔻 App.jsx

import React from "react";
import Counter from "./Counter";
import NewCounter from "./NewCounter";

class App extends React.Component {
  state = {
    count: 10,
  };

  resetCount = () => {
    this.setState(({ count }) => ({
      count: count + 10,
    }));
  };
  
  render() {
    return (
      <div>
        <div>
          <Counter count={this.state.count} />
        </div>
        <div>
          <NewCounter count={this.state.count} />
        </div>
        <button onClick={this.resetCount}>{this.state.counnt + 10}으로</button>
      </div>
    );
  }
}

export default App;

count의 초기값은 10으로, 각각 카운터 컴포넌트에 전달하고,

count를 20으로 초기화하는 기능의 버튼을 추가했다.

 

 

📁 실행

 

counter의 [카운트 증가] 버튼을 누르면 1씩 증가한다.

- Counter과 NewCounter 둘다 잘 작동한다.

 

App.jsx에 렌더링한 20으로 초기화하는 버튼을 눌러봤을때,

Counter은 초기화가 안되고

NewCounter만 20으로 초기화되었다.

 

 

- NewCounter만 getDerivedStateFromProps()로 App으로부터 갱신된 props값(=props.count)을 동기화 했기 때문.

 

- Counter는 처음 생성시에만 프로퍼티값(=props.count)를 설정하므로,

외부에서 갱신되어도 반영하지 못함.

 

 

 

 

 

context

 

부모에서 생성하여 모든 자식 컴포넌트에 전달하는 데이터.

 

- 문자열 이외의 값을 전달할때는 { } 를 사용

 

 

 

 

 

 

 


🚩 다음 포스팅

 

- 클래스형 컴포넌트

- 함수형 컴포넌트

- 배열 컴포넌트

- 컴포넌트에서의 콜백과 이벤트리스너