React는 사용자 인터페이스를 구축하기 위한 선언적이고 효율적이며 유연한 JavaScript 라이브러리입니다. “컴포넌트”라고 불리는 작고 고립된 코드의 파편을 이용하여 복잡한 UI를 구성하도록 돕습니다.
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
아이디 'root'를 가진 엘리먼트 아래에 <h1>Hello, world!</h1> 를 넣고 화면에 보여주겠다.
JSX라 하며 JavaScript를 확장한 문법입니다. UI가 어떻게 생겨야 하는지 설명하기 위해 React와 함께 사용할 것을 권장합니다. JSX라고 하면 템플릿 언어가 떠오를 수도 있지만, JavaScript의 모든 기능이 포함되어 있습니다.
1.JSX는 엘리먼트를 생성한다.
2.JSX도 표현식이다.(컴파일이 끝나면, JSX 표현식이 정규 JavaScript 함수 호출이 되고 JavaScript 객체로 인식됩니다.)
3.반드시 하나의 엘리먼트로 감싸야한다.
4.자바스크립트 코드를 적용할 땐 {} 안에 작성한다.
5.JSX 내부에서는 if문을 사용할 수 없다. IIFE or 삼항연산자 사용
6.엘리먼트의 클래스 이름을 적용할때, className을 사용 class(x)
7.React는 JSX 사용이 필수가 아니지만, 그냥 필수로 써라
8.JSX태그는 자식을 포함할 수 있다.
React 엘리먼트를 루트 DOM 노드에 렌더링하려면 둘 다 ReactDOM.render()로 전달하면 됩니다.
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
위 코드를 실행하면 화면에 “Hello, world”가 보일 겁니다.
개념적으로 컴포넌트는 JavaScript 함수와 유사합니다. “props”라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환합니다.
함수 컴포넌트
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
이 함수는 데이터를 가진 하나의 “props” (props는 속성을 나타내는 데이터입니다)
객체 인자를 받은 후 React 엘리먼트를 반환하므로 유효한 React 컴포넌트입니다.
이러한 컴포넌트는 JavaScript 함수이기 때문에 말 그대로 “함수 컴포넌트”라고 호칭합니다
클래스 컴포넌트
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
React의 관점에서 볼 때 위 두 가지 유형의 컴포넌트는 동일합니다.
React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면
JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달합니다.
이 객체를 “props”라고 합니다.
다음은 페이지에 “Hello, Sara”를 렌더링하는 예시입니다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
위 예시에서는 다음과 같은 일들이 일어납니다.
1.<Welcome name="Sara" />
엘리먼트로 ReactDOM.render()를 호출합니다.
2.React는 {name: 'Sara'}를 props로 하여 Welcome 컴포넌트를 호출합니다.
3.Welcome 컴포넌트는 결과적으로 <h1>Hello, Sara</h1>
엘리먼트를 반환합니다.
4.React DOM은 <h1>Hello, Sara</h1>
엘리먼트와 일치하도록 DOM을 효율적으로 업데이트합니다.
1.props는 읽기 전용입니다.
2.함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안 됩니다.
3.모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 합니다.
4.클래스 컴포넌트는 항상 props로 기본 constructor를 호출해야 합니다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
이 코드를 실행하면 리스트의 각 항목에 key를 넣어야 한다는 경고가 표시됩니다.
“key”는 엘리먼트 리스트를 만들 때 포함해야 하는 특수한 문자열 어트리뷰트입니다.
이제 numbers.map() 안에서 리스트의 각 항목에 key를 할당하여 키 누락 문제를 해결하겠습니다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
❗️리스트는 key값 필수로 줘야함.
Key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕습니다.
key는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 합니다.
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
Key를 선택하는 가장 좋은 방법은 리스트의 다른 항목들 사이에서 해당 항목을 고유하게
식별할 수 있는 문자열을 사용하는 것입니다. 대부분의 경우 데이터의 ID를 key로 사용합니다.
항목의 순서가 바뀔 수 있는 경우 key에 인덱스를 사용하는 것은 권장하지 않습니다.
이로 인해 성능이 저하되거나 컴포넌트의 state와 관련된 문제가 발생할 수 있습니다.
❗️map() 함수 내부에 있는 엘리먼트에 key를 넣어 주는 게 좋습니다.
Key는 배열 안에서 형제 사이에서 고유해야 하고 전체 범위에서 고유할 필요는 없습니다.
두 개의 다른 배열을 만들 때 동일한 key를 사용할 수 있습니다.
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
❗️위 예제에서 Post 컴포넌트는 props.id를 읽을 수 있지만 props.key는 읽을 수 없습니다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
JSX를 사용하면 중괄호 안에 모든 표현식을 포함 시킬 수 있으므로 map() 함수의 결과를 인라인으로 처리할 수 있습니다.
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
이 방식을 사용하면 코드가 더 깔끔해 지지만, 이 방식을 남발하는 것은 좋지 않습니다.
JavaScript와 마찬가지로 가독성을 위해 변수로 추출해야 할지 아니면 인라인으로 넣을지는 개발자가 직접 판단해야 합니다. map() 함수가 너무 중첩된다면 컴포넌트로 추출 하는 것이 좋습니다.
1.변할수 있는 값
2.컴포넌트의 사용 중 컴포텉느 내부에서 변할 수 있는 값
3.State는 클래스 컴포넌트에서만 사용 가능
Props 와 State 의 차이점
1.Props는 외부로부터 전달받은 값
2.State는 내부에서 변화하는 값
3.State는 props와 유사하지만, 비공개이며 컴포넌트에 의해 완전히 제어됩니다.
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')
);
예를 들어, 이 코드는 컴포넌트를 다시 렌더링하지 않습니다.
// Wrong
this.state.comment = 'Hello';
대신에 setState()를 사용합니다
// Correct
this.setState({comment: 'Hello'});
this.state를 지정할 수 있는 유일한 공간은 바로 constructor입니다.
React는 성능을 위해 여러 setState() 호출을 단일 업데이트로 한꺼번에 처리할 수 있습니다.
this.props와 this.state가 비동기적으로 업데이트될 수 있기 때문에
다음 state를 계산할 때 해당 값에 의존해서는 안 됩니다.
예를 들어, 다음 코드는 카운터 업데이트에 실패할 수 있습니다.
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
이를 수정하기 위해 객체보다는 함수를 인자로 사용하는 다른 형태의 setState()를 사용합니다.
그 함수는 이전 state를 첫 번째 인자로 받아들일 것이고, 업데이트가 적용된 시점의
props를 두 번째 인자로 받아들일 것입니다.
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
위 코드는 화살표 함수 사용하지 않은 것.
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
위에서는 화살표 함수를 사용했지만, 일반적인 함수에서도 정상적으로 작동합니다.
setState()를 호출할 때 React는 제공한 객체를 현재 state로 병합합니다.
예를 들어, state는 다양한 독립적인 변수를 포함할 수 있습니다.
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
별도의 setState() 호출로 이러한 변수를 독립적으로 업데이트할 수 있습니다.
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
병합은 얕게 이루어지기 때문에 this.setState({comments})는
this.state.posts에 영향을 주진 않지만 this.state.comments는 완전히 대체됩니다.
컴포넌트 life cycle에 따라 각자 Method가 호출됨
(1) constructor
컴포넌트가 mount 되기전에 실행되어야 할 부분을 실행되게끔 하는 역할.
처음 한번만 불림.
초기 state값 등을 constructor 에 설정
(2) render
DOM을 조작하고 화면에 그려지게 함
(3) componentDidMount
DOM에 render 함수 아래에 return된 요소가 추가되면, 실행되는 method
props나 setstate를 통해 상태 값 변경 가능
mount 직후 초기 렌더싱 시 한번 호출되는 method
AJAX통신과 setState를 해당 method에서 진행하는데, setState를 호출하는 경우, 렌더 함수가 두번 호출되기때문에 성능 문제가 있을 수 있음
(1) componentDidUpdate
state값이 변경되거나, DOM에 들어있는 값이 변경되면 자동으로 호출되는 method
자동으로 렌더를 되게하는 것은 아님
렌더가 호출된 다음에 변경되는 값이 있을 때 호출되는 method(최초렌더링에서는 호출되지않음)
props나 state가 변경되거나 부모컴포넌트가 리렌더될 때와 같이 컴포넌트가 업데이트 됬을 때 DOM을 조작할 때 사용
무한루프를 막기 위해서 해당 method를 사용할 때는, 조건문을 걸어주는 것이 좋음
(1) componentWillUnmount
컴포넌트가 소멸될 시점에 실행되는 method
timer와 같은 함수를 무효화하거나, 네트워크요청을 취소하는 등을 작업을 해당 method를 통해 실행
1.React의 이벤트는 소문자 대신 캐멀 케이스(camelCase)를 사용합니다.
2.JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달합니다.
<button onClick={activateLasers}>
Activate Lasers
</button>
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 콜백에서 `this`가 작동하려면 아래와 같이 바인딩 해주어야 합니다.
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
위코드에서 this.handleClick = this.handleClick.bind(this);
를 쓰는 이유
JSX 콜백 안에서 this의 의미에 대해 주의해야 합니다.
JavaScript에서 클래스 메서드는 기본적으로 바인딩되어 있지 않습니다.
this.handleClick을 바인딩하지 않고 onClick에 전달하였다면,
함수가 실제 호출될 때 this는 undefined가 됩니다.
단방향 데이터 흐름이라는 원칙에 따라, 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태 혹은 타입이 무엇인지만 알 수 있습니다. 데이터가 state로부터 왔는지, 하드코딩으로 입력한 내용인지는 알지 못합니다.
그러므로 하위 컴포넌트에서의 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 것은 마치 "역방향 데이터 흐름"과 같이 조금 이상하게 들릴 수 있습니다. React가 제시하는 해결책은 다음과 같습니다.
import React from "react";
export default class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "날 바꿔줘!"
};
this.handleChangeValue = this.handleChangeValue.bind(this);
}
handleChangeValue() {
this.setState({
value: "보여줄게 완전히 달라진 값"
});
}
render() {
return (
<div>
<div>값은 {this.state.value} 입니다</div>
<ChildComponent click={this.handleChangeValue}/> //자식에게 함수 전달
</div>
);
}
}
function ChildComponent(props) {
const handleClick = () => {
// 이 버튼을 눌러서 부모의 상태를 바꿀 순 없을까?
props.click(); //부모로 부터 전해받은 함수 실행
};
return <button onClick={handleClick}>값 변경</button>;
}