✅ 라이프 사이클이란?
1️⃣ 가상돔이란?
- DOM 트리 중 하나가 수정될 때마다 모든 DOM을 뒤지고 수정한 걸 찾고 싹 수정을 한다면, 필요없는 연산이 너무 많이 일어난다. 그래서 등장한 게 가상돔이다.
- 가상돔은 메모리 상에서 돌아가는 가짜 DOM이다.
- 가상돔의 동작 방식 : 기존 DOM과 어떤 행동 후 새로 그린 DOM(가상 돔에 올라갔다고 표현한다)을 비교해서 정말 바뀐 부분만 갈아끼워 준다. 돔 업데이트 처리가 정말 간결해진다.
2️⃣ 라이프 사이클이란?
컴포넌트의 라이트 사이클(컴포넌트의 생명주기)은 정말 중요한 개념.
👌 라이프 사이클
컴포넌트가 렌더링을 준비하는 순간부터, 페이지가 사라질 때까지
- 컴포넌트는 생성(Mount)되고 수정(업데이트)되고 사라진다(Unmount).
- 생성은 처음으로 컴포넌트를 불러오는 단계이다.
- 수정(업데이트)은 사용자의 행동(클릭, 데이터 입력 등)으로 데이터가 바뀌거나, 부모 컴포넌트가 렌더링할 때 업데이트 된다.
🔹 props가 바뀔 때
🔹 state가 바뀔 때
🔹 부모 컴포넌트가 업데이트 되었을 때(=리렌더링했을 때)
🔹 또는, 강제로 업데이트 했을 경우!(forceUpdate())를 통해 강제로 컴포넌트를 업데이트할 수 있다.
- 제거는 페이지를 이동하거나, 사용자의 행동(삭제 버튼 클릭 등)으로 인해 컴포넌트가 화면에서 사라지는 단계이다.
✅ 라이프 사이클 함수로 보는 라이프 사이클
라이프 사이클 함수는 클래스형 컴포넌트에서만 사용할 수 있다.
우리는 클래스형 컴포넌트보다 함수형 컴포넌트를 많이 쓰는데, 리액트 공식 매뉴얼에서 함수형 컴포넌트를 더 권장하기 때문이다.
// 리액트 16.8버전부터 등장한 React Hooks로 라이프 사이클 함수를 대체할 수 있다.
👌 클래스형 컴포넌트 모양
Class LifecycleEx extends React.Component{ } |
👌 생성자 함수 모양
constructor(props) { super(props); this.state = { cat_name: '나비', }; console.log('in constructor!'); } |
1️⃣ constructor()
생성자 함수라고도 부른다. 컴포넌트가 생성되면 가장 처음 호출되는 함수
2️⃣ render()
컴포넌트의 모양을 정의하는 함수
여기에서도 state, props에 접근해서 데이터를 보여줄 수 있다.
리액트 요소를 return에 넣어 반환해줬었다.
render()안에 들어갈 내용은, 컴포넌트의 모양에만 관여하는 것이 가장 좋다.
즉, state나 props를 건드려 데이터를 수정하려고 하면 안 된다.
3️⃣ componentDidMount()
컴포넌트가 화면에 나타나는 것을 마운트(Mount)한다고 표현한다.
didMount()는 마운트가 완료되었다는 뜻이다.
이 함수는 첫번째 렌더링을 마친 후에만 딱 한 번 실행되며, 컴포넌트가 리렌더링할 때는 실행되지 않는다.
보통은 이 안에서 ajax 요청, 이벤트 등록, 함수 호출 등 작업을 처리한다.
또, 이미 가상돔으로 올라간 후니까 DOM 관련 처리를 해도 된다.
4️⃣ componentDidUpdate(prevProps, prevState, snapshot)
DidMount()가 첫 렌더링 후에 호출되는 함수라면, DidUpdate()는 리렌더링을 완료한 후 실행되는 함수이다.
이 함수에 중요한 파라미터가 2개 있는데, prevProps와 prevState이다.
각각 업데이트 되기 전 props, state이며 이전 데이터와 비교할 일이 있다면 가져다 쓰면 된다.
DidUpdate()가 실행될 때도 가상돔이 실제돔으로 올라간 후니까 DOM 관련 처리를 해도 된다.
5️⃣ componentWillUnmount()
componentWillUnmount()가 호출되는 걸 보려면, app.js에서 <LivecycleEX/>를 없애봐야 한다.
삼항 연산자를 사용해서 컴포넌트를 보여주거나, 없애는 것을 조건부 렌더링이라고 부른다.
✅ Component(1)
Component는 클래스형과 함수형이 있다.
이제 클래스형 컴포넌트는 잘 쓰지 않지만 두 가지 모두 살펴봐야 한다.
🔹 리액트는 레고, 컴포넌트는 블록 같은 것
Component는 웹 사이트의 조각이고 우리는 이 조각을 모아서 웹사이트에 뿌려준다.
1️⃣ state
Component가 가지고 있는 데이터
- sparta 헤더에 들어갈 데이터는 로고 이미지 경로, 메뉴 이름 등이 있을 것이다.
- 이 데이터는 <container/>나 <footer/> 컴포넌트에서는 쓰지 않을 것이다.
- 즉, <header/> 컴포넌트에서만 쓰는 정보
state는 한 컴포넌트에서만 사용하는 정보를 주로 넣어놓고 생성, 수정하는 데이터이다.
생성도 수정도 오직 해당 컴포넌트 내에서만 이뤄진다. 내꺼니까 생성도 수정도 자유롭다
2️⃣ props
props는 Component가 부모 Component로부터 받아온 데이터이다.
<container/> 컴포넌트는 두 개의 자식 컴포넌트를 가지고 있다.
- <container/> 컴포넌트만 <imagebanner/> 컴포넌트한테 필요한 이미지 경로를 state로 가지고 있다고 가정한다.
- 이 때 <imagebanner/> 컴포넌트는 자신의 부모인 <container/> 컴포넌트로부터 이미지 경로를 전달받아서 사용해야 한다.
- <container/>가 가지고 있는 이미지 경로를 <imagebanner/>에게 전달해주면, 이 이미지 경로가 <imagebanner/> 컴포넌트의 props가 된다.
🔹 props : 부모 컴포넌트로부터 전달받은 데이터
👌 그럼 부모 컴포넌트가 가지고 있는 데이터를 <imagebanner/> 컴포넌트가 추가하거나 수정할 수 있을까?
Props로 받은 데이터는 수정할 수 없다. 남의 것이므로.
✅ Component(2)
1️⃣ 함수형 Component
👌 코딩 룰
🔹 폴더는 소문자로 시작하는 카멜케이스를 사용
🔹 JS파일, 컴포넌트 이름은 대문자로 시작하는 카멜케이스를 사용
👌 리액트 패키지를 불러온다.
import React from 'react'; |
👌 함수형 컴포넌트(1)
function Bucketlist(props){ return ( 버킷 리스트
); } |
👌 함수형 컴포넌트(2) - 화살표 함수
const BucketList = (props) => { // 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트라고 불러요.)를 반환해줍니다. return ( <div> 버킷 리스트 </div> ); } |
🔹 (props) : 부모 컴포넌트에게 받아온 데이터
js 함수가 값을 받아오는 것과 똑같이 받아온다.
👌 export
만든 함수형 컴포넌트를 export 해준다.
export 해주면 다른 컴포넌트에서 BucketList 컴포넌트를 불러다 쓸 수 있다.
export default BucketList; |
👌 App.js 로 돌아가서 BucketList 컴포넌트를 불러와 본다.
import BucketList from './BucketList'; |
BucketList 컴포넌트를 import 해온다.
🔹 import [컴포넌트명] from [컴포넌트가 있는 파일경로];
👌 컴포넌트를 <div className=""></div> 사이에 넣어준다.
function App() { return ( <div className="App"> <h1>내 버킷리스트</h1> {/* 컴포넌트를 넣어줍니다. */} <BucketList/> </div> ); } export default App; |
✅ Component(3)
1️⃣ 클래스형 Component
함수형 컴포넌트를 클래스형 컴포넌트로 바꾸려고 한다.
App.js를 클래스형으로 바꾸고, BucketList 컴포넌트에 데이터를 넘겨줘 본다.
👌 App.js를 클래스형으로 바꾸기
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/> ); } } export default App; |
2️⃣ Component에서 Component로 데이터를 넘겨주기
👌 state 값 사용하기
render() { console.log(this.state); ... } |
👌 App 컴포넌트에서 Bucket 컴포넌트로 state를 넘겨주기
render() { // this 키워드를 통해 state에 접근할 수 있다. console.log(this.state); return ( <div className="App"> <h1>내 버킷리스트</h1> {/* 컴포넌트를 넣어준다. */} {/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */} <BucketList list={this.state.list}/> </div> ); } |
🔹 BucketList.js 파일을 열고 입력
const BucketList = (props) => { console.log(props); // 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트라고 불러요.)를 반환해준다. return ( <div> 버킷 리스트 </div> ); } |
✅ 리액트에서 돔요소 가져오기
1️⃣ Ref
어떤 인풋박스에서 텍스트를 가져오고 싶을때, 리액트에선 리액트 요소에서 가져와야한다.
👌 React 요소를 가지고 오는 방법1. : React.createRef()
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; |
👌 React 요소를 가지고 오는 방법2 : React.useRef()
useRef()는 리액트 훅 중 하나이다.
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; |
✅ State 관리(1)
1️⃣ 단방향 데이터 흐름이란?
데이터는 위에서 아래로, 부모에서 자식으로 넘겨줘야 한다는 뜻
👌 왜 단방향으로 써야하는지
🔹 라이프 사이클과 함께 생각해보기
부모 컴포넌트의 state가 업데이트 되면 자식 컴포넌트도 리렌더링이 일어난다.
만약 자식 컴포넌트의 state가 바뀐 걸 부모 컴포넌트가 props로 받는다고 생각해보면, 자식 컴포넌트가 업데이트 될 때 부모 컴포넌트도 업데이트 된다. 그럼 또 자식 컴포넌트가 리렌더링 되므로..
👌 클래스형 컴포넌트에서 state 관리 - setState()
🔹 새 CRA 만들기
yarn create react-app nemo |
🔹 App.js를 class형 컴포넌트로 바꾸고 시작
// App component를 class형으로! import React from 'react'; class App extends React.Component { constructor(props){ super(props); this.state = {} } componentDidMount(){ } render(){ return ( <div className="App"> </div> ); } } export default App; |
🔹 state에 count라는 변수를 추가하고 count 숫자만큼 네모칸을 화면에 띄우기
import React from "react"; class App extends React.Component { constructor(props) { super(props); this.state = { count: 3, // 숫자넣기! }; } componentDidMount() {} render() { // 배열을 만든다. // Array.from()은 배열을 만들고 초기화까지 해주는 내장 함수 // Array.from()의 첫번째 파라미터로 {length: 원하는 길이} 객체를, // 두번째 파라미터로 원하는 값을 반환하는 콜백함수를 넘겨주면 끝! // array의 내장함수 대부분은 콜백 함수에서 (현재값, index넘버)를 인자로 씁니다. const nemo_count = Array.from({ length: this.state.count }, (v, i) => i); // 콘솔로 만들어진 배열을 확인해봅니다. 숫자가 0부터 순서대로 잘 들어갔나요? console.log(nemo_count); return ( <div className="App"> {nemo_count.map((num, idx) => { return ( <div key={idx} style={{ width: "150px", height: "150px", backgroundColor: "#ddd", margin: "10px", }} > nemo </div> ); })} </div> ); } } export default App; |
🔹 더하기, 빼기 버튼 만들기
return ( <div className="App"> {nemo_count.map((num, idx) => { return ( <div key={idx} style={{ width: "150px", height: "150px", backgroundColor: "#ddd", margin: "10px", }} > nemo </div> ); })} <div> <button>하나 추가</button> <button>하나 빼기</button> </div> </div> ); |
🔹 함수 만들기
addNemo = () => { // this.setState로 count를 하나 더해준다. this.setState({ count: this.state.count + 1 }); }; removeNemo = () => { // 네모 갯수가 0보다 작을 순 없겠죠! if문으로 조건을 걸어준다. if (this.state.count > 0) { // this.setState로 count를 하나 빼줍니다! this.setState({ count: this.state.count - 1 }); }else{ window.alert('네모가 없어요!'); } }; |
🔹 연결하기
return ( <div className="App"> {nemo_count.map((num, idx) => { return ( <div key={idx} style={{ width: "150px", height: "150px", backgroundColor: "#ddd", margin: "10px", }} > nemo </div> ); })} <div> {/* 함수를 호출한다. 이 클래스 안의 addNemo 함수를 불러오기 때문에 this.addNemo로 표기. */} <button onClick={this.addNemo}>하나 추가</button> <button onClick={this.removeNemo}>하나 빼기</button> </div> </div> ); |
✅ State 관리(2)
1️⃣ 함수형 컴포넌트에서 state 관리 - useState()
함수형 컴포넌트는 클래스형처럼 자체적으로 state를 가지고 있지 않지만, react hooks를 사용하면 state를 가질 수 있다.
👌 새 컴포넌트 만들어서 시작하기
🔹 Nemo 컴포넌트 만들기
import React from "react"; const Nemo = (props) => { // 반환할 리액트 요소가 없을 때는 null을 넘겨준다. 처음 껍데기 잡으실때도 null을 넘겨주면 굳! return null; } export default Nemo; |
🔹 App에서 <Nemo/> 불러오기
// import 먼저 하고 import Nemo from "./Nemo"; ... <div className="App"> {/*컴포넌트 불러다 쓰기*/} <Nemo/> ... ... |
🔹 useState()로 count를 state로 등록하자
// count에는 state 값이, setCount는 count라는 state 값을 수정하는 함수가 된다. // useState(초기값): () 안에 초기값을 넣어줍니다. const [count, setCount] = React.useState(3); |
🔹 뷰를 만든다 = 반환할 리액트 요소를 만든다.
const nemo_count = Array.from({ length: count }, (v, i) => i); return ( <div className="App"> {nemo_count.map((num, idx) => { return ( <div key={idx} style={{ width: "150px", height: "150px", backgroundColor: "#ddd", margin: "10px", }} > nemo </div> ); })} <div> <button>하나 추가</button> <button>하나 빼기</button> </div> </div> ); |
🔹 함수를 만든다.
const addNemo = () => { // setCount를 통해 count에 저장된 값을 + 1 해준다. setCount(count + 1); }; const removeNemo = () => { // setCount를 통해 count에 저장된 값을 - 1 해준다. // 이번엔 if문 대신 삼항 연산자로 해보기 setCount(count > 0 ? count - 1 : 0); }; |
🔹 연결하기
<div> {/* 함수를 호출합니다. */} <button onClick={addNemo}>하나 추가</button> <button onClick={removeNemo}>하나 빼기</button> </div> |
👌 최종 순서
컴포넌트 만들기 -> state를 쓰는 순서! 뷰 먼저 -> state 만들기(기본값 잡아주기) -> state를 조작하는 무언가를 만들기 -> 연결하기
'Front-End, CS 스터디 > 항해99 - React 스터디' 카테고리의 다른 글
[리액트 React] 입문 학습 자료 정리 (0) | 2022.07.28 |
---|---|
[리액트 React] 기초반 강의 4주차 정리 (2) | 2022.07.28 |
[리액트 React] 기초반 강의 3주차 정리 (0) | 2022.07.27 |
[리액트 React] 3주차 입문 주차 팀 과제 정리 (0) | 2022.07.24 |
[리액트 React] 기초반 강의 1주차 정리 (0) | 2022.07.22 |