240221 이벤트 핸들링, 컴포넌트 반복, 라이프사이클

한라봉봉·2024년 2월 21일
0

기업연계 BE 교육 TIL

목록 보기
55/58
post-thumbnail

spread 연산자

리액트에서는 신규인 경우에만 state 적용이 가능하고, virtual dom에 반영되기 때문에 spread 연산자로 신규 메모리를 할당받는다.

const obj = {a:1, b:2, c:3};
const obj2 = obj;
console.log(obj == obj2);
const obj3 = {...obj};
console.log(obj == obj3);

//console.log({...obj}); // object 복사

const a = [1,2,3,4,5];
// console.log(...a);

const b = a; //참조
console.log(a==b)

const c = [...a]; //복사 spread 연산자
console.log(c);
console.log(a==c);


복사하면서 일부 항목 덮어쓰기

const obj = {a:1, b:2, c:3};
const obj3 = {...obj,c:5};
console.log(obj3);

이벤트 핸들링

컴포넌트 반복

리스트에 키가없으면 virtual dom에 반영시 변경점 추적불가

따라서 아래와 같은 오류 발생

키를 넣어준경우 위 warning 표시되지않음

import React from 'react';

const Iteration = () => {
  const names = ['javascript', 'jQuery', 'React'];
  const nameList = names.map((name,index) => <li key={index}>{name}</li>);
  return (
    <div>
      <ul>
        {nameList}
      </ul>
    </div>
  );
};

export default Iteration;

태그내 표시되진 않음. react 내부에서 사용하는 값이기때문


setTimeout, setInterval

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title> new document </title>
  <link href="css/style.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript">
	  const init= function(){
			const buttonList = document.getElementById("buttonList");
			const aTagList = buttonList.getElementsByTagName("a");
			const bannerList = document.getElementById("bannerList");
			const liTagList = bannerList.getElementsByTagName("li");
			console.log(aTagList, liTagList);
			for (let index = 0; index < aTagList.length; index++) {
				aTagList[index].onmouseover=function(){
					console.log('sId : ',sId);

					clearInterval(sId);
					//html class -> dom className
					const myNum = this.getAttribute("num");
					changeMenu(myNum);
				};
				aTagList[index].onmouseout=function(){
					sId = setInterval(myTimer,3000);	
				}
				
			}
			//해당 메뉴를 보여주고 보여주지 않는 역할의 함수
			const changeMenu = function(myNum){
				for (let i = 0; i < aTagList.length; i++) {
					// console.log(aTagList[i].getAttribute("num"), this.getAttribute("num"));
					if(aTagList[i].getAttribute("num")==myNum){
						aTagList[i].className="on";
						liTagList[i].className="on";
					}else{
						aTagList[i].className="";
						liTagList[i].className="";
					}
				}
			}

			const myTimer = function(){
				const aTag = buttonList.getElementsByClassName("on")[0];
				const currentNum = parseInt(aTag.getAttribute("num")); //1,2,3,4
				let nextNum = currentNum + 1;
				if (nextNum >= aTagList.length + 1) {
					nextNum = 1;
				}
				changeMenu(nextNum);

			}	
			let sId = setInterval(myTimer,3000);		

		}
	
	  window.onload = init;
  </script>
 </head>
 <body style="padding:100px;">
	<div class="feature-banner__last">
		<ul id="bannerList" class="feature-banner__group">
			<li class="on">
				<a href="#"><img src="images/1.jpg" alt=""></a>
			</li>	
			<li>
				<a href="#"><img src="images/2.jpg" alt=""></a>
			</li>
			<li>
				<a href="#"><img src="images/3.jpg" alt=""></a>
			</li>
			<li>
				<a href="#"><img src="images/4.jpg" alt=""></a>
			</li>
		</ul>
		
		<div id="buttonList" class="tmp-nav img-dotted feature-banner__selector">
			<a href="#" class="on" num="1"><span>1</span></a>
			<a href="#" num="2"><span>2</span></a>
			<a href="#" num="3"><span>3</span></a>
			<a href="#" num="4"><span>4</span></a>
		</div>
	</div>
 </body>
</html>

컴포넌트 반복

import React, {useState} from 'react';

const Iteration3 = () => {
  const [names,setNames] = useState(
    [
      {id:1,text:'javascript'},
      {id:2,text:'jQuery'},
      {id:3,text:'React'}
    ]
  );
  const[nextId,setNextId] = useState(names.length+1);

  const onClick = () => {
    // push가아닌 concat으로 신규 배열생성. 신규로 데이터가 들어와야지만 화면 반영이 되기때문.
    setNames(names.concat({id:nextId, text:'Vue.js'}));
    
    setNextId(nextId+1);
  }
  const nameList = names.map((name) => <li key={name.id}>{name.text}</li>)
  return (
    <div>
      <button onClick={onClick}>add</button>
      <ul>
        {nameList}
      </ul>
    </div>
  );
};

export default Iteration3;

컴포넌트의 라이프 사이클 vs Effect 라이프 사이클

개요

모든 React 구성 요소는 동일한 수명 주기를 거칩니다.

  1. mount: 구성요소가 화면에 추가되면 마운트됩니다 .
  2. update: 구성 요소는 일반적으로 상호 작용에 대한 응답으로 새 prop이나 state를 받을 때 업데이트됩니다.
  3. unmount: 구성요소가 화면에서 제거되면 마운트 해제됩니다 .
    component의 수명주기는 위 내용을 따르지만 Effect는 그렇지 않다.
    따라서 component의 수명주기와는 별도로 각 Effect에 대해 생각해야 한다.
  • 마운트와 렌더링의 차이
    mount는 리액트가 처음으로 구성요소를 렌더링하고 실제로 초기 DOM을 빌드하는 것입니다.
    render는 DOM생성을 위해 함수가 호출될 때(혹은 클래스 기반 메서드가 호출될 때)입니다.

따라서, props나 state가 변경되어 render 될 때는 mount를 거치지 않음

1. 컴포넌트 라이프 사이클

전체는 내부 흐름 정도로 파악하고,
보통 끝단정도는 html 접근을 위해 사용하므로 자세히 알아둘것. 최근에는 함수형 컴포넌트를 도입하면서, 라이프 사이클을 3개정도로 대폭 줄였기 때문이다.

  • componentDidMount(마운트 직후)
  • componentDidUpdate(업데이트 직후)
  • componentWillUnmount(언마운트 되기 전)
    각 Effect는 위 라이프 사이클 외에도 많은 부분을 포함하고 있으므로, 위 라이프사이클과는 독립적으로 생각해야 한다

컴포넌트 라이프사이클 생성시 메서드

import React, { Component } from 'react'

class LifeCycle extends Component {
  state = {
    color:this.props.color
  }
  render() {
    console.log('render')
    return (
      <div>
        <h1 style={{color:this.props.color}}>
          props: {this.props.color} <br />
          state: {this.state.color}
        </h1> 
      </div>
    )
  }
}

export default LifeCycle;
import React, { Component } from 'react';
import LifeCycle from './LifeCycle';

class App extends Component {
  state = {
    color:'red'
  }
  render() {
    return (
      <div>
        <LifeCycle color={this.state.color} />
        <button onClick={() => this.setState({color:'blue'})}>blue</button>
      </div>
    );
  }
}

export default App;

컴포넌트 라이프 사이클 수정시 메서드

import React, { Component } from 'react'

class LifeCycle extends Component {
  state = {
    color:null
  }
  constructor(props){
    super(props);
    console.log('constructor');
  }
  componentDidMount(){
    console.log('componentDidMount');
  }

  static getDerivedStateFromProps(nextProps,prevState){
    console.log('getDerivedStateFromProps',nextProps,prevState);
    if(nextProps.color != prevState.color){
      return{color:nextProps.color}
    }
    return null;
  }

  getSnapshotBeforeUpdate(prevProps,prevState){
    console.log('getSnapshotBeforeUpdate',prevProps,prevState);
    if(prevProps.color !== this.props.color){
      return prevProps.color;
    }
    return null;
  }

  componentDidUpdate(prevProps,prevState,snapshot){
    console.log('componentDidUpdate',prevProps,prevState,snapshot);
  }


  render() {
    console.log('render')
    return (
      <div>
        <h1 style={{color:this.props.color}}>
          props: {this.props.color} <br />
          state: {this.state.color}
        </h1> 
      </div>
    )
  }
}

export default LifeCycle;

  shouldComponentUpdate(nextProps,nextState){
    console.log('shouldComponentUpdate',nextProps,nextState);
    return nextProps.color === 'green'? false : true;
  }

2. 컴포넌트 라이프사이클과 분리하여 Effect의 관점에서 생각하기

구성 요소를 채팅 서버에 연결하는 Effect 예제

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  useEffect(() => {
  // Effect의 본문은 동기화를 시작하는 방법을 지정합니다.
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
   // Effect에서 반환된 정리 함수는 동기화를 중지하는 방법을 지정합니다.
      connection.disconnect();
    };
  }, [roomId]);
  // ...
}

사용자는 드롭다운에서 다른 방을 선택합니다(예: "travel"). React는 UI를 업데이트합니다.

function ChatRoom({ roomId /* "travel" */ }) {
  // ...
  return <h1>Welcome to the {roomId} room!</h1>;
}

Effect와 컴포넌트 관점

ChatRoom 컴포넌트 관점에서 아래와 같은 라이프 사이클이 발생

  1. roolId가 "general"로 설정된 상태로 ChatRoom이 마운트됨
  2. roolId가 "travel"로 설정된 상태로 ChatRoom이 업데이트됨
  3. roolId가 "music"로 설정된 상태로 ChatRoom이 업데이트됨
  4. ChatRoom이 마운트 해제됨

컴포넌트 생명주기의 각 시점에서 Effect는 서로 다른 작업을 수행함

  1. Effect가 "general" 방에 연결됨
  2. Effect가 "general" 방과 연결 연결이 끊어지고 "travel" 방에 연결됨
  3. Effect가 "travel" 방과 연결 연결이 끊어지고 "music" 방에 연결됨
  4. Effect가 "music" 방과 연결이 끊어짐
profile
백엔드 개발공부 로그를 기록합니다

0개의 댓글