클래스형 컴포넌트
- 클래스형 컴포넌트는 리액트 생명주기 함수와 컴포넌트 구성 요소를 모두 포함하고 있음
- 클래스형 컴포넌트는 Component와 PureComponent의 두 종류의 클래스를 사용함
- Component
- Component 클래스는 프로퍼티, state와 생명주기 함수가 들어있는 구조의 컴포넌트를 만들 때 사용함
- Component 클래스로 만들어진 컴포넌트는 항상 render() 함수를 호출함
- Component 예시 :
import React from 'react'; class MyComp extends React.Component { constructor(props) { super(props); console.log('생성'); } componentDidMount() { } myMethod() { } render() { } } export default MyComp;
- PureComponent
- PureComponent 클래스는 Component 클래스를 상속받은 클래스임
- PureComponent 클래스는 shouldComponentUpdate() 함수를 얕은 비교를 하도록 재정의함
- PureComponent 클래스로 만들어진 컴포넌트는 얕은 비교를 통해 데이터가 변경된 경우에만 render() 함수를 호출함
- PureComponent 예시 :
└ ⓐ : 얕은 비교를 위해 shallow-equal 라이브러리의 shallowEqaul() 함수가 사용됨import React from 'react'; import shallowEqual from 'shallow-equal'; // ⓐ export class PureComp extends React.Component { shouldComponentUpdate(nextProps, nextState) { return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState) // ⓑ } }
└ ⓑ : shallowEqaul() 함수로 현재 프로퍼티, state를 갱신한 프로퍼티, state와 얕은 비교를 하고 있음
- 얕은 비교 예시 :
우선, 얕은 비교를 위해
를 입력하여 shallowEqaul 라이브러리를 설치한다.yarn add shallow-equal
비교를 위한 변수를 선언한다.
import shallowEqual from 'shallow-eqaul'; const obj = { name: 'hoony'}; const mylist = [1, 2, 3, obj]; const list1 = [1, 2, 3, obj]; const list2 = [1, 2, 3, { name: 'hoony' }];
깊은 비교 :
└ 결과값으로 false가 나옴mylist === list1
└ mylist와 list1은 같은 요소를 가지고 있지만 각각 새롭게 정의된 배열이기 때문에 false이다.
배열 요소 하나하나를 모두 비교해 볼 수 없기 때문에 얕은 비교를 사용한다.
얕은 비교 :
└ 결과값으로 true가 나옴shallowEqaul(mylist, list1)
└ 결과값으로 false가 나옴shallowEqual(list1, list2)
└ list2의 마지막 요소는 obj와 같은 내용을 포함하고 있지만 새 객체이기 때문이다.
* 이처럼 내용물을 모두 비교하지 않는 이유는 비교 검사 작업이 성능에 영향을 끼치기 때문에
Pure Component 클래스로 선언한 컴포넌트에서는 shouldComponentUpdate() 함수에서 shallowEqual() 함수로 얕은 비교를 하여 데이터의 변경이 있으면 화면을 새로 출력하고, Component 클래스로 선언한 컴포넌트는 비교 과정 없이 항상 새로 출력함
함수형 컴포넌트
- state가 없는 함수형 컴포넌트라고 하며 SFC(stateless Functional Component)라고 부름
- 함수형 컴포넌트는 state를 포함하지 않음
- 함수와 동일한 구조를 가지며 입력받은 프로퍼티와 컨텍스트를 이용하여 화면을 출력함
- 함수형 컴포넌트 예시 :
└ ⓐ : 클래스형 컴포넌트의 this.props와 동일함import React from 'react'; import PropTypes from 'prop-types'; function SFC(props, context) { const { somePropVal } = props; // ⓐ const { someContextVal } = context; // ⓑ return <h1>Hello, {somePropVal}</h1> } SFC.propTypes = { somePropVal : PropTypes.any }; SFC.defaultProps = { somePropVal : 'default val' }; export default SFC;
└ ⓑ : 클래스형 컴포넌트의 this.context와 동일함
└ 함수형 컴포넌트는 상위 컴포넌트로부터 전달받은 프로퍼티와 컨텍스트만을 사용하기 때문에 클래스 선언이 없음
└ 함수형 컴포넌트는 state와 생명주기 함수를 사용할 수 없음
배열 컴포넌트
- 자바스크립트 배열은 다양한 자료형을 저장할 수 있음
- 게시판 또는 유튜브 영상 목록 등을 구현할 때 사용됨
- 배열 컴포넌트를 위한 map() 함수 사용 :
- map() 함수를 사용하면 배열로 저장된 데이터를 JSX로 변경할 수 있음
- 배열 컴포넌트를 위한 map() 함수 사용 예시 :
const todoList = [ { taskName: '공부하기', finished: false }, { taskName: '밥먹기', finished: true } ]; const todos = todoList.map(todo => <div>{todo.taskName}</div>;
- map() 함수를 사용한 배열 데이터를 컴포넌트의 프로퍼티에 전달 :
const todoList = [ { taskName: '공부하기', finished: false }, { taskName: '밥먹기', finished: true } ]; const todos = todoList.map(todo => <TodoTask taskName={todo.taskName} />);
- 배열 컴포넌트를 위한 map() 함수 응용 (/src/ex/List.jsx) :
import React from 'react'; class List extends React.Component { render() { const priceList = [1000, 2000, 3000, 4000]; const prices = priceList.map((price) => <div>가격 : {price}원</div>); return ( <div> <lavel>가격 목록</lavel> {prices} </div> ); } } export default List;
- App 컴포넌트 수정 (/src/App.js) :
import React from 'react'; import List from './ex/List'; class App extends React.Component { render() { return ( <List /> ) } } export default App;
- 결과 :
- map() 함수 사용해서 배열 컴포넌트 출력
- render() 함수에서 바로 map() 함수를 이용해 배열을 출력함
- 기존의 방법으로 배열에 저장된 컴포넌트 출력 예시 :
... render() { const todoList = [ { taskName: '공부하기', finished: false }, { taksName: '밥먹기', finished: true } ]; return ( <div> <div>{todo[0].taskName}</div> <div>{todo[1].taskName}</div> </div> ); } ...
- 위의 코드를 map() 함수를 사용하여 바꾸기 :
... render() { const todoList = [ { taskName: '공부하기', finished: false }, { taskName: '밥먹기', finished: true } ]; const todos = todoList.map(todo => <div>{todo.taskName}</div> return ( <div> {todos} </div> ); } ...
- 위의 코드를 더 간략하게 바꾸기 :
└ 배열 컴포넌트는 배열 요소의 개수만큼 반복하기 때문에 성능에 영향을 많이 줌... render() { const todoList = [ { taskName: '공부하기', finished: false }, { taskName: '밥먹기', finished: true } ]; return ( <div> {todoList.map(todo => <div>{todo.taskName}</div>} </div> ); } ...
└ 따라서, 배열 컴포넌트에는 키값을 key로 꼭 정의해주어야 함
└ 키 값을 정의하여 출력한 배열 컴포넌트는 다시 출력해야 하는 경우
리액트 엔진이 기존의 컴포넌트를 재활용하기 때문에 성능을 높임
- 키값을 정의한 배열 컴포넌트 예시 :
... render() { const todoList = [ { taskName: '공부하기', finished: false }, { taskName: '밥먹기', finished: true } ]; return ( <div> {todoList.map((todo, i) => <div key={`tl_${i}`}>{todo.taskName}</div>} </div> ); } ...
- 위의 코드에서 filter() 함수를 추가 :
└ filter() 함수를 추가하여 배열 항목에서 finished의 값이 false인 항목을 제외하는 등의... render() { const todoList = [ { taskName: '공부하기', finished: false }, { taskName: '밥먹기', finished: true } ]; return ( <div> {todoList.filter(todo => todo.finished).map((todo, i) => ( <div key={`tl_${i}`}>{todo.taskName}</div> ))} </div> ); }
항목을 변형하는 경우 인덱스 번호를 키로 사용하면 키값이 함께 변경됨
└ 이 때, '밥먹기'의 키 값은 'tl_1'에서 'tl_0'로 바뀌게 됨
└ 리액트가 '밥먹기' 항목의 컴포넌트를 재활용하지 못하기 때문에 비효율 문제가 발생함
└ 이를 해결하기 위해 키값으로 고유한 배열 항목을 사용하면 됨
- 위의 코드 수정 (src/ex/Todo.jsx) :
import React from 'react'; class Todo extends React.PureComponent { render() { const todo = [ { taskName: '공부하기', finished: false }, { taskName: '밥먹기', finished: true } ]; return ( <div> {todo.map((todo) => <div key={todo.taskName}>{todo.taskName}</div>)} </div> ); } } export default Todo;
- App 컴포넌트 수정 (/src/App.js) :
import React from 'react'; import Todo from './ex/Todo'; class App extends React.Component { render() { return ( <Todo /> ) } } export default App;
- 결과 :
- render() 함수에서 여러 개의 JSX 노드 반환
- render() 함수는 트리 구조의 노드를 반환함
- 리액트는 트리 구조의 노드 외에도 배열 구조의 노드를 반환할 수 있음
- Option 컴포넌트 3개 반환해야 하는 예시 :
└ 이 코드는 동작하지 않음... render() { return ( <input type="radio" name="option1" value="1" label="1개"> <input type="radio" name="option2" value="2" label="2개"> <input type="radio" name="option3" value="3" label="3개"> ); } ...
└ render() 함수는 트리 구조의 노드를 반환해야 하기 때문
└ 리액트 16.3 버전까지 render() 함수는 트리 구조의 노드 1개만 반환할 수 있었음... render() { return ( <div> <input type="radio" name="option1" value="1" label="1개"> <input type="radio" name="option2" value="2" label="2개"> <input type="radio" name="option3" value="3" label="3개"> </div> ); } ...
└ 따라서, 여러 개의 노드를 반환하기 위해서는 위의 코드처럼 의미 없는 최상위 노드를 추가해야 했음
└ 16.3 버전 이후 의미 없는 노드를 추가하는 것이 개선되었음... render() { return ( <React.Fragment> <input type="radio" name="option1" value="1" label="1개"> <input type="radio" name="option2" value="2" label="2개"> <input type="radio" name="option3" value="3" label="3개"> </React.Fragment> ); } ...
└ React.Fragment 컴포넌트가 추가됨
└ React.Fragment는 위와 같이 표현되기도 함... render() { return ( <> <input type="radio" name="option1" value="1" label="1개"> <input type="radio" name="option2" value="2" label="2개"> <input type="radio" name="option3" value="3" label="3개"> </> ); } ...
└ React.Fragment 컴포넌트는 여러 노드를 반환할 때 사용됨
└ 따라서, 이 컴포넌트는 HTML로 변환되지 않음
└ map() 함수를 사용하여 감싸는 부분을 생략할 수 있음... render() { return [1, 2, 3].map((num) => ( <input type="radio" name="option1" key={`${num}`} value={num} label={`${num}개`} /> )); } ...
'React > Component' 카테고리의 다른 글
컴포넌트 (6) - 콜백 함수, 이벤트 처리 (0) | 2021.09.11 |
---|---|
컴포넌트(4) - 생명주기 (0) | 2021.09.08 |
컴포넌트(3) - 상태 관리 (0) | 2021.09.07 |
컴포넌트(2) - 프로퍼티 (0) | 2021.09.07 |
컴포넌트 (1) - JSX, 컴포넌트 (0) | 2021.08.31 |