const [todoList, setTodoList] = useState([]);
// todoList -> 첫번째 아이템은 상태값
// setTodoList -> 두번째 아이템은 상태값 변경함수
useState
를 사용하면 컴포넌트의 상태값을 추가할 수 있다. 매개변수는 상태값의 초기값 의미 (투두에서는 빈배열을 초기값으로 넣어줌)
useState
는 배열을 반환한다.
상태값 변경함수를 호출해서 상태가 변경되면 리액트는 UI에 반영해준다.
배열을 표현할때는 key 값을 입력해줘야한다. 리액트가 화면을 효율적으로 업데이트 할 수 있다.
배열비구조화문법
비구조화문법은 각아이템을 변수로 만들 수 있다.
const person = ['mike',23]
const [name,age] = person
const arr1 = [1,2,3]
const arr2 = [...arr1]
// arr2 = [arr1[0],arr1[1],arr1[2]] 처럼 나열하는 것과 같음
명령형 프로그래밍
/ 리액트는 선언형 프로그래밍
선언형 프로그래밍
은 명령형보다 추상화단계가 높다.리액트 컴포넌트에서는 UI 데이터를 속성값
이나 상태값
으로 관리해야한다.
리액트가 값의 상태 변경 사실을 알기위해서는 상태값으로 관리해줘야한다.
리액트에서 상태값을 설정하는 방법
const [color,setColor] = useState('red')
초기값은 red로 설정
function onClick() {
setColor('blue');
}
값을 변경할때는 setColor함수를 호출해준다.
Title.js
=> 자식 컴포넌트
import React from 'react';
export default function Title(props) {
return <p>{props.title}</p>
}
function Title({title}) {
console.log('title render');
return <p>{title}</p>
}
export default React.memo(Title)
props
는 부모가 전달해주는 속성값React.memo
속성값 title이 변경될때만 이 컴포넌트가 렌더링 된도록 도와주는 함수counter.js
=>Title
에게 값을 전달해주는 부모 컴포넌트
import React, { useState } from 'react';
import Title from './Title'
export default function Counter() {
const [count,setCount] = useState(0);
function onClick() {
setCount(count + 1)
}
return (
<div>
<Title title={`현재 카운드:${count}`}/>
<button onClick={onClick}>증가</button>
</div>
)
}
부모컴포넌트가 렌더링 될 때 마다 자식도 렌더링 된다.
react.memo
를 사용한다.속성값
은 불변변수
export default function Counter() {
const [count,setCount] = useState({value1:0,value2:0,value:0});
function onClick() {
setCount({ ...count, value:count.value + 1 });
// count를 객체에 있는 모든 속성을 풀어놓고, 변경하고자 하는 값만 할당해주는 것
}
return (
<div>
<Title title={`현재 카운드:${count.value}`}/>
<button onClick={onClick}>증가</button>
</div>
)
}
상태값
은 불변변수가 아님
컴포넌트에서는 기본적으로 리액트 요소를 반환할 수 있다.
export default function App() {
return <div> 안녕하세요</div>
}
물론 컴포넌트도 리액트 요소로 반환할 수 있다.
export default function App() {
return <Counter/>
}
단순히 문자열, 숫자, 배열도 반환할 수 있다.
export default function App() {
return 'abc'
}
배열로 반환할때는 리액트 요소가 key를 갖고있어야 한다.
export default function App() {
return [<p key={1}>안녕</p>,<p key={2}>하세요.</p>]
}
key는 렌더링을 효율적으로 하기위해서 필요한 값이다.
리액트가 이 값을 이용해서 virtualDom에서의 연산을 효율적으로 할 수 있다.
Fragment
컴포넌트가 반환할 수 있는 값 중 Fragment
가 있다.
export default function App() {
return (
<React.Fragment>
<p>안녕</p>
<p>하세요.</p>
</React.Fragment>
)
}
export default function App() {
return (
<>
<p>안녕</p>
<p>하세요.</p>
</>
)
}
축약형으로도 사용. 아무것도 입력하지 않아도 동작한다.
export default function App() {
return (
<>
<p>안녕</p>
{null}
{true}
{false}
</>
)
}
return (
<div>
{count.value > 0 && <Title title={`현재 카운드:${count.value}`}/> }
<button onClick={onClick}>증가</button>
</div>
)
&&
은 왼쪽의 조건이 모두 만족이 돼야 뒤에 있는 것이 렌더링 된다는 뜻Potal
컴포넌트에서는 리액트 Potal
을 반환할 수 있다.
public
> index.html
<div id="root"></div>
<div id="somthing"></div>
root가 말고 다른 멀리 떨어진 엘리먼트를 렌더링하고 싶을때 사용한다.
Potal
을 사용하기 위해서 react-dom
에 있는 함수를 사용해야 한다.
import ReactDOM from 'react-dom'
export default function App() {
return (
<>
<Counter></Counter>
{ReactDOM.createPortal(
<div>
<p>안녕하세요.</p>
<p>실전 리액트 프로그래밍입니다.</p>
</div>,
document.getElementById('something'),
)}
</>
)
}
html
에 있는 요소를 입력하면 된다.Potal
은 보통 모달
을 위해서 사용되기도 한다.
리액트 요소
는 리액트가 UI를 표현하는 수단
- 리액트는 렌더링 성능을 위해서
가상돔
이라는 것을 활용
- 빠른 렌더링을 위해서 돔 변경을 최소화 해줘야 함- 리액트는 메모리에
가상돔
을 올려놓고 이전과 이후의 가상돔을 비교한다.
-변경된 부분
만 실제돔에 반영하는 전략
리액트 요소로부터 가상돔을 만들어서 실제 돔에 반영할 변경사항을 찾는 과정을 알아보자.
const element = (
<a key="key1" style={{width:100}} href="http://google.com">click here</a>
)
console.log(element);
위의 리액트 요소를 출력한 결과
const consoleLogResult = {
type: 'a', // a 태그
key: 'key1', // key 속성을 넣어줬기떄문
ref: null,
props: {
href:'http://google.com',
style:{
width:100,
},
children:'click here',
}
// ...
}
function Title({title,color}) {
return <p style={{ color }}> {title} </p>
}
DOM 요소와는 다르게 type
속성에 Title이라는 컴포넌트 함수가 출력되는 것이 확인된다.
const element = <Title title="안녕하세요" color="blue"></Title>
const consoleLogResult = {
type : Title,
props : {title:'안녕하세요',color:'blue'}
}
const element = <a href="http://google.com">click here</a>
element.type = 'b' // 에러발생
// 리액트 요소는 변경할 수가 없다.
App.js
export default function App() {
const [seconds, setSeconds] = useState(0);
useEffect(()=>{
setTimeout(() => {
setSeconds(v=>v+1) // 1초에 한번씩 seconds 라는 상태값을 증가 시킴
}, 1000);
});
return (
<div>
<h1 style={{color:seconds % 2 ? 'red' : 'blue' }>안녕하세요.</h1>
<h2>지금까지 {seconds}초가 지났습니다.</h2>
</div>
)
}
<div key={seconds}>
{seconds}
값으로 변경해보았다.Counter 컴포넌트를 만든뒤
export default function Counter() {
const [count,setCount] = useState(0);
function onClick() {
setCount(count + 1)
}
return (
<div>
<p>{`현재 카운트: ${count}`}</p>
<button onClick={onClick}>증가</button>
</div>
)
}
<CounterChange key={seconds}/>
CounterChange
컴포넌트의 key 값에 적용해보았다.0
으로 계속 초기화가 된다.unmount
라고 부르고 컴포넌트가 추가되는 것을 mount
라고 부른다.mount
됐을 때는 useState
에서 첫번째 매개변수로 입력된 초기값이 상태값으로 할당된다.unmount
와 mount
를 반복한다.조건부렌더링도 비슷한 효과를 볼 수 있다.
return (
<div>
{seconds % 2 === 0 && <CounterChange/>}
<h1 style={{color:seconds % 2 ? 'red' : 'blue' }}>안녕하세요.</h1>
<h2>지금까지 {seconds}초가 지났습니다.</h2>
</div>
)
unmount
되면서 현재 카운트가 0으로 초기화 되는 것을 볼 수 있다.counter.js
export default function Counter() {
const [count,setCount] = useState(0);
function onClick() {
setCount(count + 1)
}
return (
<div>
<p>{`현재 카운트: ${count}`}</p>
<button onClick={onClick}>증가</button>
</div>
)
}
Count 컴포넌트를 생성해서 App.js에 연결한다.
jsx 문법코드 -> 객체 트리 구조
렌더단계
와 커밋단계
를 거친다.렌더단계
는 실제 DOM에 반영할 변경사항을 파악하는 단계커밋단계
는 파악된 변경사항을 실제 DOM에 반영하는 단계두개의 컴포넌트가 있다 ( 코드생략 )
function Todo() {
const [priority,setPriority] = userState('high;)
// Todo에서는 priority라는 상태값 사용
}
const Title = React.memo()
// Title 컴포넌트는 memo 함수 사용했기 때문에 Title의 속성값이 변경될때만 렌더링 된다.
이렇게 Todo 컴포넌트로 만들어진 리액트 요소는 객체 트리구조
형태를 갖는다 (생략)
type에는 Todo 컴포넌트 함수가 있을 것이다.
리액트는 렌더링 결과를 얻기 위해서 Todo함수를 호출한다.
- 그 결과 또한 객체 트리구조
형태를 가진다
- 호출된 Todo함수 객체 트리구조
를 살펴봤을때 Title 컴포넌트가 존재한다. 때문에 해당 리액트 요소는 실제 돔으로 만들 수 없다.
- 리액트 요소 트리가 실제 돔으로 만들어지기 위해서는 모든 리액트 요소의 타입 속성 값이 문자열
이여야 한다.
Title 컴포넌트를 렌더링한 결과 모든 리액트 요소의 타입 속성은 문자열이 된 것을 알 수 있다. -> 실제 돔을 만들 수 있다.
이와 같이 실제 돔을 만들 수 있는 리액트 요소 트리를 가상돔이라고 할 수 있다.
최초의 리액트 요소 트리로부터 가상돔을 만들고 이전 가상돔과 비교해서 실제 돔에 반영할 내용을 결정하는 단계를 렌더단계
라고 부른다.
참고로 최종 리액트 요소 트리를 만들기 위해서 치환되는 중간에 Todo나 Title같은 컴포넌트의 리액트 요소도 메모리에 저장돼서 렌더단계의 효율을 높이는데 사용된다.
가상돔은 UI에서 변경된 부분을 빨리 찾기위한 개념이므로 컴포넌트의 리액트 요소(Title,Todo)도 가상돔의 일부라고 생각할 수 있다.
리액트는 화면을 업데이트할때 이전의 가상돔과 현재가상돔을 비교해서 변경된 부분만 실제 돔에 반영한다.
지금 작성한 코드는 최초 렌더링 결과이므로 이대로 실제 돔에 반영된다.
- 이후에 다시 렌더링 될때 지금 만들어진 가상돔과 비교해서 변경된 부분만 실제돔에 반영
이렇게 중요하지만 다소 번거로운 작업을 리액트가 내부적으로 해주는 것
렌다단계는 렌더함수를 호출(ReactDOM.render()
)하거나 또는 컴포넌트 내부에서 상태값 변경함수를 호출해서 시작될 수 있다.
🔍 강의출처
실전 리액트 프로그래밍