const element = <h1>Hello, world</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
하단의 예제는 setInterval() 콜백을 이용해 매 초마다 ReactDOM.render()를 호출한다.
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
if (isPacked) {
return null;
}
return <li className="item">{name}</li>;
return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);
return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);
messageCount && <p>New messages</p> -> XmessageCount > 0 && <p>New messages</p> -> 예시
<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
const listItems = people.map(person => <li>{person}</li>);
<ul>로 래핑된 컴포넌트의 listItems를 반환한다.return <ul>{listItems}</ul>;
예시
const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];
const chemists = people.filter(person =>
person.profession === 'chemist'
);
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
return <ul>{listItems}</ul>;
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
요소에 유저가 정의한 컴포넌트를 나타낼 수 있다.
const element = <Welcome name="Sara" />;
하단의 예시는 "Hello, Sara"를 페이지에 렌더링한다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
<Welcome name="Sara" /> 요소로 ReactDOM.render() 를 호출한다.{name: 'Sara'} 를 props로 하여 Welcome 컴포넌트를 호출한다.<h1>Hello, Sara</h1> 요소를 반환한다.<h1>Hello, Sara</h1> 과 일치하도록 DOM을 효율적으로 업데이트한다.예시) Welcome을 여러번 렌더링하는 App 컴포넌트를 만들 수 있다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
기존코드
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
export default function Profile() {
return (
<img
src="https://i.imgur.com/MK3eW3Am.jpg"
alt="Katherine Johnson"
/>
)
}
export default 접두사는 표준 JavaScript 구문이다.function Profile() { }을 사용하면 Profile이라는 이름의 JavaScript 함수를 정의할 수 있다.<img/> 는 HTML처럼 작성되었지만 실제로는 JavaScript이다. = JSX 구문export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
function Avatar({ person, size }) {
// person과 size는 이곳에서 사용가능합니다.
}
function Avatar({ person, size = 100 }) {
// ...
}
size={undefined}일 때 사용된다.size={null}이거나 size={0}일 경우, 사용되지 않는다.function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
함수 코드
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
클래스로 변환
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
date를 props에서 state로 옮기기
1. render() 메서드 내의 this.props.date를 this.state.date로 바꾼다.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
<Clock /> 요소에서 date prop을 삭제한다.결과
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
컴포넌트가 DOM에 최초로 렌더링 될 때 타이머 설정
DOM에서 컴포넌트를 삭제했을 때 타이머 해제
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
전체 코드
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
<Clock /> 이 ReactDOM.render() 에 전달될 때, React는 Clock 컴포넌트의 생성자 함수를 호출한다. Clock이 현 시간 화면에 보여질 때, 현 시간을 포함하는 this.state 객체를 초기화하며, 이 state는 추후 업데이트한다.render() 메서드를 호출한다. React가 화면에 보여줄 부분을 나타낸다. 그다음 React는 Clock 의 렌더링 출력과 일치하도록 DOM을 업데이트합니다.componentDidMount() 라이프 훅을 호출한다. 내부에서, Clock 컴포넌트는 브라우저에게 컴포넌트의 tick() 메서드를 초당 한번 호출하는 타이머를 설정하라고 요구한다.tick() 메서드를 호출한다. 내부에서, Clock 컴포넌트는 현재 시간을 포함하는 객체로 setState() 를 호출하여 UI 업데이트를 예약한다. setState() 호출 덕분에, React는 상태가 변경된 걸 알고 있고, render() 메서드를 다시 한번 호출해 스크린에 무엇이 있어야 하는 지 알 수 있다. 이번에는, render() 메서드 내의 this.state.date 가 달라지므로 렌더 출력에 업데이트된 시간이 포함된다. React는 그에 따라 DOM을 업데이트한다.componentWillUnmount() 라이프사이클 훅을 호출하고 타이머를 멈춘다.setState()를 사용해 수정한다.this.props 및 this.state가 비동기로 업데이트될 수 있으므로, 다음 state를 계산할 때 해당 값을 신뢰해서는 안된다.// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
})
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
setState() 를 호출할 때, React는 현재 state와 제공한 객체를 병합한다.
1) state는 여러 독립적인 변수를 가질 수 있다.
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
2) 개별 setState()를 호출하여 아이템을 각각 업데이트할 수 있다.
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
예) 온도 계산기
화씨, 도씨 입력하는 두 input 값이 동기화 되기를 원한다.
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={temperature}
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(temperature)} />
</fieldset>
);
}
}
-> 두 번째 input 추가
1. TemperatureInput 컴포넌트 추출
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
class Calculator extends React.Component {
render() {
return (
<div>
<TemperatureInput scale="c" />
<TemperatureInput scale="f" />
</div>
);
}
}
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
render() {
// Before: const temperature = this.state.temperature;
const temperature = this.props.temperature;
// ...handleChange(e) {
// Before: this.setState({temperature: e.target.value});
this.props.onTemperatureChange(e.target.value);
// ...class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
[ 전체 코드 ]
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Calculator />);
<input> 이 바뀔 때마다 onChange 로 정의된 함수를 호출한다. 이 케이스에서는 TemperatureInput 컴포넌트의 handleChange 메서드가 이 역할을 수행한다.handleChange 메서드는 새 이상적인 값으로 this.props.onTemperatureChange() 를 호출한다. onTemperatureChange 를 포함한 props는 부모 컴포넌트인 Calculator 에서 제공한다.onTemperatureChange -> handleCelsiusChangeonTemperatureChange -> handleFahrenheitChangethis.setState() 를 호출해 스스로를 다시 렌더링하도록 요청한다.render 메서드를 호출하여 UI가 어떻게 보여야하는 지 알아내고 두 입력 값은 현재 온도와 활성 scale에 따라 다시 계산된다.(온도 변환 수행)render 메서드를 호출함으로써 UI가 어떻게 보여야하는 지 알아낸다.input은 현재 값을 받고, 다른 input은 변환 후 온도를 업데이트합니다.모든 업데이트는 같은 단계를 통하기 때문에 input은 동기화 상태를 유지한다.