DOM은 html 단위 하나하나를 객체로 생각하는 모델이다.
예를 들면 'div'라는 객체는 텍스트 노드, 자식 노드 등등, 하위의 어떤 값을 가지고 있다. 이런 구조를 트리 구조라고 한다. DOM이 트리구조라는 뜻이다.
DOM트리 중 하나가 수정될 때마다 모든 DOM을 뒤지고 수정한 것을 찾고 모두 수정한다면 필요없는 연산이 너무 많이 일어나게 된다. 그래서 등장한 것이 가상돔이다.
가상돔은 메모리 상에서 돌아가는 가짜 DOM이다.
가상돔의 동작 방식: 기존 DOM과 어떤 행동 후 새로 그린 DOM(가상돔에 올라감)을 비교해서 정말 바뀐 부분만 갈아끼워준다. 돔 업데이트 처리가 간결해진다.
처음 페이지에 진입했을 때나 데이터가 변했을 때 DOM을 새로 그린다.
DOM은 사이트 구조에 따라 가상돔으 ㄹ쓰는 것보다 훨씬 성능이 좋을 수 있고(빠를 수 있고), 느릴 수도 있다.
컴포넌트의 라이프 사이클(= 컴포넌트 생명주기)은 정말 중요한 개념이다.
컴포넌트가 렌더링을 준비하는 순간부터, 페이지에서 사라질때까지가 라이프 사이클이다.
라이프 사이클 도표
컴포넌트는 생성되고 수정(업데이트)되고 사라진다.
생성은 처음으로 컴포넌트를 불러오는 단계이다.
수정(업데이트)는 사용자의 행동(클릭, 데이터 입력 등)으로 데이터가 바뀌거나. 부모 컴포넌트가 렌더링할 때 업데이트 된다.
- props가 바뀔 때
- stage가 바뀔 때
- 부모 컴포넌트가 업데이트 되었을 때(리렌더링했을 때)
- 또는 강제로 업데이트 했을 경우(forceUpdate()를 통해 강제로 컴포넌트를 업데이트 할 수 있다.)
제거는 페이지를 이동하거나, 사용자의 행동(삭제 버튼 클릭 등)으로 인해 컴포넌트가 화면에서 사라지는 단계이다.
라이프 사이클을 아는 것은 중요하지만 클래스형 컴포넌트보다 함수형 컴포넌트를 쓰는 이유는 리액트 공식 매뉴얼에서 함수형 컴포넌트를 더 권장하기 때문이다.
(리액트 16.8버전부터 등장한 React Hooks으로 라이프 사이클 함수를 대체할 수 있기 때문이다.)
생성자 함수라고도 부른다. 컴포넌트가 생성되면 가장 처음 호출된다.
컴포넌트의 모양을 정의한다. 여기에서도 state, props에 접근해서 데이터를 보여줄 수 있다.
리액트 요소를 return에 넣어 반환해주는데 render() 안에 들어갈 내용은 컴포넌트 모양에만 관여하는 것이 가장 좋다. 즉, state나, props를 건드려 데이터를 수정하려고 하면 안된다.
컴포넌트가 화면에 나타나는 것을 마운트(Mount)한다고 표현한다.
didMount()는 마운트가 완료되었다는 의미이다.
이 함수는 첫 번째 렌더링을 마친후에만 딱 한 번 실행된다.
컴포넌트가 리랜더링할 때는 실행되지 않는다.
보통은 이 안에서 ajax 요청, 이벤트 등록, 함수 호출 등 작업을 처리한다.
또, 이미 가상돔이 실제돔으로 올라간 후니까 DOM 관련 처리를 해도 된다.
DidMount()가 첫 렌더링 후에 호출 되는 함수라면, DidUpdate()는 리렌더링을 완료한 후 실행되는 함수이다. 이 함수에 중요한 파라미터가 2개 있는데, preProps와 prevState이다.
각각 업데이트 되기 전 props, state이다. 이전 데이터와 비교할 일이 있을 때 쓰면 된다.
DidUpdate()가 실행될 때도 가상돔이 실제돔으로 올라간 후이기 때문에 DOM 관련 처리를 해도 된다.
삼항연산자를 사용해서 컴포넌트를 보여주거나, 없애는 것을 조건부렌더링이라고 부른다.
리액트는 레고, 컴포넌트는 블록이다.
웹사이트를 조각냈을 때 조각 하나하나를 컴포넌트라고 한다.
conponent는 웹 사이트의 조각이고, 우리는 이 조각을 모아서 웹사이트에 뿌려준다.
component에서 데이터를 관리하는 방법
리액트 코딩룰 : 폴더는 소문자로 시작하는 카멜케이스를 사용
JS파일, 컴포넌트 이름은 대문자로 시작하는 카멜케이스를 사용
// 리액트 패키지를 불러온다.
import React from 'react';
// 함수형 컴포넌트는 이렇게 쓸 수도 있고
// function Bucketlist(props){
// return (
// <div>버킷 리스트</div>
// );
// }
// 이렇게 쓸 수도 있다. =>가 들어간 함수를 화살표 함수라고 한다.
// () 안에 props는 부모 컴포넌트에게 받아온 데이터이다.
// js 함수가 값을 받아오는 것과 똑같이 받아오네요.
const BucketList = (props) => {
// 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트)를 반환해준다.
return (
<div>
버킷 리스트
</div>
);
}
// 함수형 컴포넌트를 export 해준다.
// export 해주면 다른 컴포넌트에서 BucketList 컴포넌트를 불러다 쓸 수 있다.
export default BucketList;
import React from 'react';
import logo from './logo.svg';
import './App.css';
// BucketList 컴포넌트를 import 해온다.
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketList from './BucketList';
// 클래스형 컴포넌트
class App extends React.Component {
constructor(props){
super(props);
// App 컴포넌트의 state를 정의해준다.
this.state = {
list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
};
}
// 랜더 함수 안에 리액트 엘리먼트를 넣어줍니다!
render() {
return (
<div className="App">
<h1>내 버킷리스트</h1>
{/* 컴포넌트를 넣어줍니다. */}
<BucketList/>
</div>
);
}
}
export default App;
12)Component에서 Component로 데이터 넘겨주기
render() {
console.log(this.state);
...
}
this 키워드는 context 객체와 연관이 있다.
함수나 클래스 안에서 사용하면 this를 쓴 위치에 있는 값을 가지고 온다고 생각하자.
- 컴포넌트에 props 넘겨주기
render() { // this 키워드를 통해 state에 접근할 수 있어요. console.log(this.state);
return (
<div className="App">
<h1>내 버킷리스트</h1>
{/* 컴포넌트를 넣어줍니다. */}
{/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */}
<BucketList list={this.state.list}/>
</div>
);
}
- 컴포넌트 확인
```javascript
const BucketList = (props) => {
console.log(props);
// 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트라고 불러요.)를 반환해줍니다.
return (
<div>
버킷 리스트
</div>
);
}
// 콘솔에서 확인해보기!
src폴더에 style.css파일을 만들고 그안에 스타일을 정의하고 그 파일 불러다 사용한다.
class 대신 className을 준다.
APP.js에서는 import를 했는데, BucketList.js에서는 style.css를 import하지 않는 이유
yarn add styled-components
16)styled-components란?
컴포넌트 스타일링 기법
리액트에서 어떤 인풋박스에서 텍스트를 가져오고 싶다면 리액트 요소에서 가져온다.
import React from "react";
import logo from "./logo.svg";
// BucketList 컴포넌트를 import 해옵니다.
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketList from "./BucketList";
import styled from "styled-components";
// 클래스형 컴포넌트는 이렇게 생겼습니다!
class App extends React.Component {
constructor(props) {
super(props);
// App 컴포넌트의 state를 정의해줍니다.
this.state = {
list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
};
// ref는 이렇게 선언합니다!
this.text = React.createRef();
}
componentDidMount(){
// 콘솔에서 확인해보자!
console.log(this.text);
console.log(this.text.current);
}
// 랜더 함수 안에 리액트 엘리먼트를 넣어줍니다!
render() {
return (
<div className="App">
<Container>
<Title>내 버킷리스트</Title>
<Line />
{/* 컴포넌트를 넣어줍니다. */}
{/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */}
<BucketList list={this.state.list} />
</Container>
<div>
<input type="text" ref={this.text}/>
</div>
</div>
);
}
}
const Container = styled.div`
max-width: 350px;
min-height: 80vh;
background-color: #fff;
padding: 16px;
margin: 20px auto;
border-radius: 5px;
border: 1px solid #ddd;
`;
const Title = styled.h1`
color: slateblue;
text-align: center;
`;
const Line = styled.hr`
margin: 16px 0px;
border: 1px dotted #ddd;
`;
export default App;
import React from "react";
import styled from "styled-components";
const BucketList = ({ list }) => {
const my_lists = list;
const my_wrap = React.useRef(null);
console.log(my_wrap); // 콘솔로 확인해봐요!
window.setTimeout(() => { // 1초 뒤에는?!
console.log(my_wrap);
}, 1000);
return (
<div ref={my_wrap}>
{my_lists.map((list, index) => {
return <ItemStyle key={index}>{list}</ItemStyle>;
})}
</div>
);
};
const ItemStyle = styled.div`
padding: 16px;
margin: 8px;
background-color: aliceblue;
`;
export default BucketList;
20)단방향 데이터 흐름이란?
데이터는 위에서 아래로, 부모에서 자식으로 넘겨줘야 한다는 소리이다.
클래스형 컴포넌트의 state를 업데이트할 때 사용하는 함수
(1)새 CRA 만들기
yarn create react-app nemo
(2)App.js를 class형 컴포넌트로 바꾼다.
(3)Array.from
함수형 컴포넌트는 클래스형처럼 자체적으로 state를 가지고 있지 않지만, react hooks를 사용하면 state를 가질 수 있다.
컴포넌트를 만들고 state를 쓰는 순서(뷰 먼저 만든 다음 state를 만들고(기본값도 잡아주기) 그 다음 state를 조작하는 무언가를 만들어서 연결한다.)