dom을 직접 건드리는 작업은 번거롭다.
React의 발상 : dom을 전부 날려버리고 다시 만들어서 보여주자
문제점 : 매번 이렇게 하게되면 다양한 문제가 발생
해결 : 메모리게 가상DOM을 만든다.
업데이트가 필요한 부분만 가상DOM으로 수정한다.
그 이후 REACT의 알고리즘을 통해 다른 부분을 감지하여 실제 DOM에 패치 시켜준다.
Node.js
자바스크립트 런타임인 Node.js 를 기반으로 만들어진 Webpack 과 Babel 같은 도구들을 사용하기 위해서 설치한다.
npm install
npx create-react-app my-app
cd my-app
npm start
JSX : 리액트 컴포넌트에서 xml 형식의 값을 반환해는 것이다.
컴포넌트 : 일종의 UI 조각으로 쉽게 재사용 가능하다.
DOM 내부에 리액트 컴포넌트를 렌더링
ReactDOM.render(, document.getElementById('root'));
닫혀 있는 태그
태그는 꼭 닫혀있어야 한다.(Self Closing 태그)<input /> //꼭 감싸져야 하는 태그 //두 개 이상의 태그는 무조건 하나의 태그로 감싸져야 한다. import React from 'react'; import Hello from './Hello'; function App() { return ( <> //리액트의 Fragment <Hello /> <div>안녕히계세요</div> </> ); } export default App;
JSX 안에 자바스크립트 값 사용하기
JSX 내부에 자바스크립트 변수를 보여줘야 할 때에는 {}으로 감싸서 보여준다import React from 'react'; import Hello from './Hello'; function App() { const name = 'react'; return ( <> <Hello /> <div>{name}</div> </> ); } export default App; ) }
style 과 className
1) JSX에서 인라인 스타일은 객체 형태로 작성해야한다.
2) background-color처럼 - 로 구분되어 있는 이름들은 backgroundColor처럼 camelCase 형태로 네이밍 해줘야 한다.
3) CSS class 를 설정 할 때에는 class= 가 아닌 className= 으로 설정해야 한다.import React from 'react'; import Hello from './Hello'; import './App.css'; function App() { const name = 'react'; const style = { backgroundColor: 'black', color: 'aqua', fontSize: 24, // 기본 단위 px padding: '1rem' // 다른 단위 사용 시 문자열로 설정 } return ( <> <Hello /> <div style={style}>{name}</div> <div className="gray-box"></div> </> ); } export default App;
주석
JSX 내부의 주석은 / * 이런 형태로 */ 작성한다.
열리는 태그 내부에서는 // 이런 형태로도 작성 가능하다.
props
props는 properties의 줄임말로 어떠한 값을 컴포넌트에게 전달해줘햐 할때 사용한다.
비구조화 할당
//App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" color="red"/>
);
}
export default App;
//Hello.js
import React from 'react';
function Hello({ color, name }){
return <div style={{ color }}안녕하세요 { name }</div>
}
export default Hello;
defaultProps 로 기본값 설정
컴포넌트에 props 를 지정하지 않았을 때 기본적으로 사용 할 값을 설정하고 싶다면 컴포넌트에 defaultProps 라는 값을 설정하면 된다.
//Hello.js
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
//App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<>
<Hello name="react" color="red"/>
<Hello color="pink"/> //'안녕하세요 이름없음'이 핑크색 글씨로 나온다.
</>
);
}
export default App;
props.children
컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 땐, props.children 을 조회하면 된다.
//Wrapper.js
import React from 'react';
function Wrapper() {
const style = {
border: '2px solid black',
padding: '16px',
};
return (
<div style={style}>
</div>
)
}
export default Wrapper;
//App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';
function App() {
return (
<Wrapper>
<Hello name="react" color="red"/>
<Hello color="pink"/>
</Wrapper>
);
}›
export default App;
Wrapper 태그 내부에 Hello 컴포넌트 두개를 넣었는데, 브라우저를 확인하면 다음과 같이 Hello 컴포넌트들은 보여지지 않는다. 내부의 내용이 보여지게 하기 위해서는 Wrapper에서 props.children을 렌더링해주어야 한다.
//Wrapper.js
import React from 'react';
function Wrapper({ children }) {
const style = {
border: '2px solid black',
padding: '16px',
};
return (
<div style={style}>
{children}
</div>
)
}
export default Wrapper;
조건부 렌더링이란
조건부 렌더링이란, 특정 조건에 따라 다른 결과물을 렌더링 하는 것을 의미한다.
삼항 연산자, &&
<div style={{ color }}>
{ isSpecial ? <b>*</b> : null }
안녕하세요 {name}
</div>
<div style={{ color }}>
{isSpecial && <b>*</b>}
안녕하세요 {name}
</div>
&&를 사용하면 첫번째로 나오는 false값을 반환하며 없다면 마지막 값을 반환한다.
이벤트 설정
리액트에서 엘리먼트에 이벤트를 설정해줄때에는 on이벤트이름={실행하고싶은함수} 형태로 설정해줘야 한다. 이벤트를 설정할 때에는 함수 타입의 값을 넣어주어야 한다.
동적인 값 끼얹기, useState
useState 라는 함수를 사용하면 컴포넌트에서 상태를 관리 할 수 있다.
카운터 만들기
//App.js
import React from "react";
import Counter from "./Counter";
function App() {
return <Counter />;
}
export default App;
//Counter.js
//useState 함수 불러오가
import React, { useState } from "react";
function Counter() {
//useState를 사용 할 때에는 상태의 기본값을 파라미터로 넣어서 호출.
//함수를 호출하면 배열이 반환.
//첫번째 원소는 현재 상태, 두번째 원소는 Setter 함수.
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(number + 1);
};
const onDecrease = () => {
setNumber(number - 1);
};
return (
<div>
//Setter 함수는 파라미터로 전달 받은 값을 최신 상태로 설정.
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
input 태그 : 리액트에서 사용자가 입력 할 수 있는 태그이다.
input 창 만들기
//App.js
import React from 'react';
import InputSample from './InputSample';
function App() {
return (
<InputSample />
);
}
export default App;
//InputSample.js
import React, { useState } from 'react';
function InputSample() {
const [text, setText] = useState('');
const onChange = (e) => {
//
setText(e.target.value);
};
const onReset = () => {
setText('');
};
return (
<div>
<input onChange={onChange} value={text} />
<button onClick={onReset}>초기화</button>
<div>
<b>값: {text}</b>
</div>
</div>
);
}
export default InputSample;
1) input의 onChange를 사용하면 이벤트 객체 e를 파라미터로 받아올 수 있다.
2) 이 객체의 e.target은 이벤트가 발생한 DOM을 가리킨다.
3) e.target.value를 조회하면 현재 input의 value값을 알 수 있다.
input의 개수가 여러개일 때
1) input에 name을 설정하고 이벤트가 발생했을 때 이 값을 참조하는 것이다.
2) useState에서는 문자열이 아니라 객체 형태의 상태를 관리해야한다.
여러개의 input 창 만들기
//InputSample.js
import React, { useState } from 'react';
function InputSample() {
const [inputs, setInputs] = useState({
name: '',
nickname: ''
});
const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출
const onChange = (e) => {
const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
setInputs({
...inputs, // 기존의 input 객체를 복사한 뒤
[name]: value // name 키를 가진 값을 value 로 설정
});
};
const onReset = () => {
setInputs({
name: '',
nickname: '',
})
};
return (
<div>
<input name="name" placeholder="이름" onChange={onChange} value={name} />
<input name="nickname" placeholder="닉네임" onChange={onChange} value={nickname}/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
}
export default InputSample;
1) 리액트에서 객체를 업데이트하게 될 때에는 기존 객체를 직접 수정하면 안되고, 새로운 객체를 만들어서, 새 객체에 변화를 주어야 한다.
setInputs({
...inputs,
[name]: value
});
불변성을 지킨다
1) 불변성을 지켜야만 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고 이에 따라 필요한 리덴더링이 진행된다.
2) 컴포넌트 업데이트 성능 최적화를 제대로 할 수 있다.
useRef
1) 리액트를 사용하는 프로젝트에서도 가끔씩 DOM 을 직접 선택해야 하는 상황이 발생 할 때가 있다(특정 엘리먼트의 크기를 가져와야 한다던지, 스크롤바 위치를 가져오거나 설정해야된다던지, 또는 포커스를 설정해줘야된다던지 등)
2) 그럴 때 useRef 라는 Hook 함수를 사용한다.
초기화 버튼 클릭했을 때 이름 input 에 포커스가 잡히는 기능 구현하기
import React, { useState, useRef } from 'react';
function InputSample() {
const [inputs, setInputs] = useState({
name: '',
nickname: ''
});
const nameInput = useRef();
const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출
const onChange = e => {
const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
setInputs({
...inputs, // 기존의 input 객체를 복사한 뒤
[name]: value // name 키를 가진 값을 value 로 설정
});
};
const onReset = () => {
setInputs({
name: '',
nickname: ''
});
nameInput.current.focus();
}; //input에 포커스를 하는 focus() DOM API를 호출해주었다.
return (
<div>
<input
name="name"
placeholder="이름"
onChange={onChange}
value={name}
ref={nameInput}
/>
<input
name="nickname"
placeholder="닉네임"
onChange={onChange}
value={nickname}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
}
export default InputSample;
1) useRef()를 사용하여 Ref 객체를 만들고,
2) 이 객체를 우리가 선택하고 싶은 DOM에 ref 값으로 설정해준다.
3) 그러면, Ref 객체의 .current 값은 우리가 원하는 DOM 을 가르키게 됩니다.
동적인 배열 렌더링
동적인 배열을 렌더링해야 할 때에는 자바스크립트 배열의 내장함수 map() 을 사용한다.
Map에서 Key가 필요한 이유
1)리액트에서 배열을 렌더링 할 때에는 key 라는 props 를 설정해야 한다. key 값은 각 원소들마다 가지고 있는 고유값으로 설정을 해야한다.
2) Map에 key 값이없다면 중간의 값이 바뀌었을때 그 하위 값들이 전부 변한다. key값을 사용한다면 key를 이용해 중간의 값을 추가하게 된다.
배열을 렌더링하기
//UserList.js
import React from 'react';
function User({ user }) {
return (
<div>
<b>{user.username}</b> <span>({user.email})</span>
</div>
);
}
function UserList() {
const users = [
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com'
},
{
id: 2,
username: 'tester',
email: 'tester@example.com'
},
{
id: 3,
username: 'liz',
email: 'liz@example.com'
}
];
return (
<div>
{users.map(user => (
<User user={user} key={user.id} />
))}
</div>
);
}
export default UserList;