const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<h1>Hello, world!</h1>);
const element = <h1>Hello, World!</h1>;
Javascript를 확장한 문법.
React에서는 본질적으로 렌더링 로직이 UI로직(이벤트가 처리되는 방식, 시간에 따라 State가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등)과 연결됨.
별도 파일에 마크업과 로직을 넣어 각각의 technologies를 인위적으로 분리하는 대신, 둘다 포함하는 "컴포넌트"라고 부르는 느슨하게 연결된 유닛으로 Cocern을 Separating한다.(컴퓨터 프로그램을 구별된 부분으로 분리시키는 디자인 원칙으로, 각 부문은 개개의 관심사를 해결한다. 관심사란 컴퓨터 프로그램 코드에 영향을 미치는 정보의 집합)
const name = 'Chaeyun';
const element = <h1>Hello, {name}</h1>;
중괄호 안에 모든 JS 표현식을 넣을 수 있음.
엘리먼트는 컴포넌트의 '구성요소'
React로 구현된 앱은 일반적으로 하나의 Root DOM 노드가 있음
React 엘리먼트를 렌더링 하기 위해서는 우선 DOM 엘리먼트를 ReactDOM.createRoot()
에 전달한 다음, React 엘리먼트를 root.render()
에 전달해야 한다.
<div id="root"></div>
const root = ReactDOM.createRoot(
document.getElementById('root')
);
const element = <h1>Hello, world</h1>;
root.render(element);
React 엘리먼트는 Immutable object임.
UI를 업데이트하는 방법은 새로운 엘리먼트를 생성하고 이를 root.render()로 전달하는 거임.
아래는 setInterval() 콜백을 이용해 초마가 root.render()를 호출하게 하는 로직.
const root = ReactDOM.createRoot(
document.getElementById('root')
);
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
root.render(element);
}
setInterval(tick, 1000);
=> 매초 전체 UI를 다시 그리도록 만들었지만 React DOM은 내용이 변경된 텍스트 노드만 업데이트 시켜줌.
React는 컴포넌트를 통해 UI를 여러 조각으로 나누고 재사용함.
개념적으로 JS 함수와 유사. props
라는 임의의 입력을 받아 화면에 어떻게 표시할지 Element를 반환함.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
const root = ReactDOM.CreateRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;
root.render();
자신의 출력에 다른 컴포넌트를 참조할 수 있음. 즉, 모든 단계에서 동일한 추상 컴포넌트를 사용할 수 있음.
React에서는 Button, Form, Dialog, Screen 등의 것들이 흔히 컴포넌트로 표현됨.
function App() {
return(
<div>
<Welcome name="Sara"/>
<Welcome name="Chaeyun" />
</div>
);
}
모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 한다.
State는 Props와 유사하지만 비공개이며 컴포넌트에 의해 완전히 제어함.
class Clock extends React.Component {
constructor(props) {
super(props); // 클래스 컴포넌트는 항상 props로 기본 constructor를 호출해야 함.
this.state = {date: new Date()}
}
// Clock이 렌더링될 떄마다 타이머 설정
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000);
}
// Clock에 의해 생성된 DOM이 삭제될 때마다 타이머 해제
componentWillUnmount() {
clearInterval(this.timerID);
}
render() {
return (
<div>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
setState()
사용하면 됨constructor
부모 컴포넌트나 자식 컴포넌트 모두 특정 컴포넌트가 유상태인지 또는 무상태인지 알 수 없고, 그들이 함수나 클래스로 정의되었는지에 대해서 관심을 가질 필요가 없음.
#top-down(하향식)
, #unidirectional(단방향식)
데이터 흐름. 모든 State는 항상 특정 컴포넌트가 소유하고 있으며 오직 아래에 있는 컴포넌트에만 영향을 미친다.
- React 이벤트는 소문자 대신 camelCase를 사용.
- JSX를 사용하여 함수로 이벤트 핸들러 전달
- false를 반환해도 기본 동작을 방지 X
// HTML
<button onclick="activateLasers()">
Activate Lasers
</button>
// JSX
<button onClick={activateLasers}>
Activate Lasers
</button>
this.handleClick
을 바인딩 하지 않고 onClick
에 전달하면 함수가 실제로 홀출될 때 this
는 undefined
됨.class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 콜백에서 `this`가 작동하려면 아래와 같이 바인딩 해주어야 합니다.
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
// 화살표 함수
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row></button>
// Function.prototype.bind -> 추가 인자(e)가 자동으로 전달됨
<button onClicnk={this.deleteRow.bind(thid, id)}>Delete Row</button>
function Greeing(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeing />;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
// Try changing to isLoggedIn={true}:
root.render(<Greeting isLoggedIn={false} />);
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Page />);
class NumberList extends React.Component {
const numbers = props.numbers;
render () {
return (
numbers.map((number) =>
<li key={number.id}>
{number}
</li>);
);
}
}
Key는 엘리먼트 리스트를 만들 떄 포함해야하는 특수 문자열 어트리뷰트.
React가 어떤 항목을 업데이트할지 식별하는 것을 도움.
안정적인 고유성을 위해 배열 내부 엘리먼트에 지정해야 함.
대부분 데이터의 ID로 key를 사용.
배열로 사용할 때만 의미 있음.
Key는 Siblings 사이에서만 고유하면 됨.
React 에 의해 값이 제어되는 입력 폼 엘리먼트
HTML에서 input, textarea, select 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트함.
vs
React에서는 변경할 수 있는 state가 일반적으로 컴포넌트 state에 유지되며 setState()에 의해 업데이트 됨
// input
<input type="text" value={this.state.value} onChange={this.handleChange} />
// textarea
<textarea value={this.state.value} onChange={this.handleChange} />
// select
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
</select>
value 어트리뷰트는 폼 엘리먼트에 설정되므로 표시되는 값은 항상 this.state.value가 되고 React state는 신뢰 가능한 단일 출처 (single source of truth)가 된다.
값이 변하면 handleChange를 동작하게 하여 state를 바꿔줌
select 태그에서는 html의 selected 어트리뷰터를 사용하는 대신 value 를 사용.