→ DOM 을 직접 건드리면서 작업을 하는 경우 처리해야 할 이벤트도 다양해지고, 관리해야 할 상태값도 다양해지고, DOM 도 다양해지게 된다면 업데이트를 하는 규칙도 많이 복잡해진다
node.js / yarn
Webpack
Babel
기본 구조
import React from 'react'; // 리액트를 불러와줘야 한다
function Hello() { // 리액트 컴포넌트
return <div>안녕하세요</div> // XML형식의 값을 반환해준다 (JSX라고 부름)
}
export default Hello; // 다른 컴포넌트에서 사용할 수 있도록 내보내줌
사용법
import React from 'react';
import Hello from './Hello'; // 불러와서
function App() {
return (
<div>
<Hello /> // html태그처럼 사용
<Hello /> // 재사용 가능
<Hello />
</div>
);
}
export default App;
컴포넌트와 실제 index.html을 연결
ReactDOM.render - 브라우저에 있는 실제 DOM 내부에 리액트 컴포넌트를 렌더링하겠다는 것을 의미
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// <App>컴포넌트를 index.html의 root태그에 연결한다
리액트에서 생김새를 정의할 때 사용하는 문법으로,
리액트 컴포넌트 파일에서 XML형태로 코드를 작성하면 babel 이 JSX를 JavaScript 로 변환을 해준다
HTML의 태그는 이미 약속한 태그들만 사용 가능하지만, XML 태그는 사용자임의로 만들 수 있다.
XML는 어떠한 데이터를 설명하기 위해 이름을 임의로 지은 태그로 데이터를 감싼다
마크업언어를 정의하기 위한 언어
jsx문법 규칙
const style = {
backgroundColor: 'black',
color: 'aqua',
fontSize: 24, // 기본 단위 px
padding: '1rem' // 다른 단위 사용 시 문자열로 설정
}
<div style={style}>{name}</div>
어떠한 값(부모 컴포넌트의 state, 사용자가 주고싶은(?)값)을 컴포넌트에게 전달해줘야 할 때 props를 사용한다
전달
function App() {
return (
<Hello name="react" /> // name이란 이름의 값을 Hello컴포넌트에 전달
);
}
사용
function Hello(props) { // 인자로 props객체를 받아서
return <div>안녕하세요 {props.name}</div> // jsx내부에서 원하는 값을 꺼내 사용한다
}
// 비구조화할당 문법으로 사용하려는 것만 꺼내 편하게 사용할 수 있다
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
defaultProps
컴포넌트에 props 를 전달하지 않았을 때 기본적으로 사용 할 값을 설정할 수 있다
Hello.defaultProps = {
name: '이름없음'
}
props.children
컴포넌트 태그 사이의 넣은 content값 또는 컴포넌트를 조회할 수 있다
특정 조건에 따라 다른 결과물을 렌더링 하는 것
삼항연산자
특정 조건에 따라 보여줘야 하는 내용이 다를 때 사용
// 넘어온 props값을 조건으로 렌더링하기
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{ isSpecial ? <b>*</b> : null }
안녕하세요 {name}
</div>
);
}
// JSX 에서 null, false, undefined 를 렌더링하게 된다면 아무것도 나타나지 않게 된다
&&연산자
단순히 특정 조건이 true
이면 보여주고, 그렇지 않다면 보여주지 않을 때 사용
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{isSpecial && <b>*</b>}
안녕하세요 {name}
</div>
);
}
props 이름만 작성하고 값 설정을 생략한다면, 이를 true
로 설정한 것으로 간주
컴포넌트에서 보여줘야 하는 내용이 사용자 인터랙션에 따라 바뀌어야 할 때 상태(동적인 값)를 사용해 구현한다
이벤트 설정
리액트에서 엘리먼트에 이벤트를 설정해줄때에는 on이벤트이름={실행하고싶은함수}
형태로 설정해주어야 한다
return (
<div>
<h1>0</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
<button onClick={onDecrease()}>-1</button> // 함수를 실행하면 안된다!
</div>
);
동적인 값 끼얹기, useState
리액트의 Hooks 중 하나인 useState라는 함수를 사용해 상태 관리를 할 수 있다
import React, { useState } from 'react'; // useState함수 불러옴
function Counter() {
// 초기값을 넣어서 실행하면 초기값으로 셋팅된 상태변수와,
// 들어온 파라미터로 상태변수를 업데이트 시켜주는 함수 2개의 값을 가진 배열 반환
const [number, setNumber] = useState(0);
import React, { useState } from 'react';
function Counter() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(number + 1); // 이벤트 핸들러 함수에 setter함수 실행
}
const onDecrease = () => {
setNumber(number - 1);
}
return (
<div>
<h1>{number}</h1> // 상태는 jsx에서 사용
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
e
를 파라미터로 받아와서 사용할 수 있다e.target.value
를 조회하면 현재 input에 입력한 값이 무엇인지 알 수 있다useState
를 통해 관리해준다onChange
이벤트를 사용한다import React, { useState } from 'react';
function InputSample() {
const [text, setText] = useState('');
const onChange = (e) => {
setText(e.target.value); // input에 작성된 값이 바뀌면 상태 값을 업데이트 시켜준다
};
const onReset = () => {
setText('');
};
return (
<div>
<input onChange={onChange} value={text} /> // value 값도 설정해주어야 상태가 바뀌었을때 input의 내용도 업데이트 된다
<button onClick={onReset}>초기화</button>
<div>
<b>값: {text}</b>
</div>
</div>
);
}
export default InputSample;
*input내용 없음 → input내용 채워짐 → 상태 바뀜 → 바뀐 상태로 input에 값 렌더링 됨?
새로고침 했을 때 input의 값이 그대로 유지되기 위해?
input의 개수가 여러 개가 됐을 때, useState
와 onChange
를 여러 개 사용할 수 있지만 좋은 방법이 아니다
name
을 설정하고 이벤트가 발생했을 때 이 값을 참조한다useState
에서는 문자열이 아니라 객체 형태로 상태를 관리해주어야 한다불변성 지키기
리액트 상태에서 객체를 수정해야 할 때에는 직접 수정하는 것이 아니라 깊은 복사(...)를 이용해 복사된 새로운 객체를 만들어주고, 그 새로운 객체를 수정한 후 상태에 업데이트해야한다
= 결국 새로운 객체로 업데이트해주는 것
setInputs({
...inputs, // 기존의 input 객체를 복사한 뒤
[name]: value // 복사한 값을 수정함
});
왜 이렇게 하냐?
객체를 직접 수정할 경우 메모리주소가 바뀌지 않아 리액트가 상태가 업데이트 됐음을 인지하지 못해 리렌더링을 하지 않는다.