리액트에서는 신규인 경우에만 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);
따라서 아래와 같은 오류 발생
키를 넣어준경우 위 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 내부에서 사용하는 값이기때문
<!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;
모든 React 구성 요소는 동일한 수명 주기를 거칩니다.
따라서, props나 state가 변경되어 render 될 때는 mount를 거치지 않음
전체는 내부 흐름 정도로 파악하고,
보통 끝단정도는 html 접근을 위해 사용하므로 자세히 알아둘것. 최근에는 함수형 컴포넌트를 도입하면서, 라이프 사이클을 3개정도로 대폭 줄였기 때문이다.
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;
}
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>;
}