JSX
const element = <h1>Hello, world!</h1>;
- JavaScript를 확장한 문법
- JavaScript 모든 기능이 포함
- React "element"를 생성한다.
React + JSX
- 리엑트에서는 본질적으로 렌더링 로직이 UI 로직과 연결된다는 사실을 받아들인다.
- 리엑트는 별도의 파일에 마크업과 로직을 넣어 기술을 인위적으로 분리하는 대신, 둘 다 포함하는 "컴포넌트" 라고 부르는 느슨하게 연결된 유닛으로 관심사를 분리한다.
- JSX는 HTML 보다는 JS에 가깝기 때문에 React DOM은 HTML 어트리뷰트 이름 대신 카멜케이스 규칙을 사용한다.
JSX에 표현식 포함하기
- JSX에 중괄호 안에는 유효한 모든 JS 표현식을 넣을 수 있다.
- JSX도 표현식이다.
JSX는 객체를 표현한다.
- Babel은 JSX를
React.createElement()
호출로 컴파일한다.const element = ( <h1 className="greeting"> Hello, world! </h1> );
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
- 이 두 구조는 동일하다.
React.createElement()
는 버그가 없는 코드를 작성하는 데 도움이 되도록 몇 가지 검사를 수행하고 객체를 생성한다. 생성된 객체를 "React Element"라고 하며 화면에서 보고 싶은 것을 나타내는 표현이라 생각하면 된다. React는 이 객체를 읽어서 DOM을 구성하고 최신 상태로 유지하는 데 사용한다.
엘리먼트 렌더링
- 엘리먼트는 React 앱의 가장 작은 단위이다. 화면에 표시할 내용을 기술한다.
- 브라우저 DOM 엘리먼트와 달리 React 엘리먼트는 일반 객체이며(plain obj) 쉽게 생성할 수 있다. React DOM은 엘리먼트와 일치하도록 DOM을 업데이트한다.
엘리먼트
- React 엘리먼트는 불변객체이다. 엘리먼트를 생성한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다.
- 엘리먼트는 특정 시점의 UI를 보여준다.
root DOM 노드
<div id="root"></div>
- 이 안에 들어가는 모든 엘리먼트를 React DOM에서 관리하기 때문에 이것을 루트 DOM 노드라고 부른다.
- React로 구현된 애플리케이션은 일반적으로 하나의 루트 DOM 노드가 있다.
- React 엘리먼트를 루트 DOM 노드에 렌더링하려면 둘 다
ReactDOM.render()
로 전달하면 된다.
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
Components와 Props
Component
- JS 함수와 유사하다.
- "props"라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환한다.
함수 컴포넌트와 클래스 컴포넌트
- 컴포넌트를 정의하는 가장 간단한 방법은 JS 함수를 작성하는 것이다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
- 이 함수는 React 엘리먼트를 반환하므로 유효한 React 컴포넌트이다. 이러한 컴포넌트는 JS 함수이기 때문에 말 그대로 "함수 컴포넌트" 라고 한다.
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
- 위 두가지는 동일한 컴포넌트이다.
컴포넌트 렌더링
const element = <Welcome name="Sara" />;
- React 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있다.
- React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달한다. 이 객체를 props라고 한다.
컴포넌트 이름은 항상 대문자로 시작한다.
- React는 소문자로 시작하는 컴포넌트를 DOM 태그로 처리한다.
컴포넌트 합성
- 컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있다. 이는 모든 세부 단계에서 동일한 추상 컴포넌트를 사용할 수 있음을 의미한다.
- React앱에서는 버튼, 폼, 다이얼로그, 화면 등의 모든 것들이 흔히 컴포넌트로 표현된다.
props
- props의 이름은 사용될 문맥이 아닌 컴포넌트 자체의 관점에서 짓는 것을 권장한다.
- props는 읽기 전용 이다. (순수 함수이다.)
- 모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 한다.
State and Lifecycle
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>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
<Clock />
엘리먼트가 ReactDOM.render()로 전달되었을 때 React는 Clock 컴포넌트의 constructor를 호출한다. Clock 에서는 this.state를 초기화한다.- React에서는 Clock 컴포넌트의 render() 메서드를 호출한다. 이를 통해 React는 화면에 표시되어야 할 내용을 알게 된다. 그 다음 React는 Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트 한다.
- Clock 출력값이 DOM에 삽입되면, React는
componentDidMount()
생명주기 메서드를 호출한다. 그 안에서 Clock 컴포넌트는 매초 컴포넌트의tick()
메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요청한다. - 매초 브라우저가 tick() 메서드를 호출한다. 그 안에서 Clock 컴포넌트는
setState()
에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트를 진행한다.setState()
호출 덕분에 React는 state가 변경된 것을 인지하고 화면에 표시될 내용을 알아내기 위해render()
메서드를 다시 호출한다. 이 때render()
메서드 안의 this.state.date가 달라지고 렌더링 출력값은 업데이트된 시각을 포함한다. React는 이에 따라 DOM을 업데이트 한다.
- Clock 컴포넌트가 DOM으로부터 한 번이라도 삭제된 적이 있다면 React는 타이머를 멈추기 위해 componentWillUnmount() 생명주기 메서드를 호출한다.
State를 올바르게 사용하기
- 직접 State를 수정하지 말기 (직접 수정하게 되면 변화를 알아차리기 힘들어진다.)
- 다음 state를 계산할 때 해당 값에 의존해서는 안된다. (비동기적으로 업데이트될 수 있기 때문에)
// Correct this.setState((state, props) => ({ counter: state.counter + props.increment }));
- 이 setState는 이전 state를 첫 번째 인자로 받아들이고 업데이트가 적용된 시점의 props를 두 번째 인자로 받아들인다.
State 업데이트는 병합된다.
setState()
를 호출할 때 React는 제공한 객체를 현재 state로 병합한다.- 병합은 얕게 이루어진다.
데이터는 아래로 흐른다.
- 모든 state는 항상 특정한 컴포넌트가 소유하고 있으며 그 state로부터 파생된 UI, 데이터는 오직 트리구조에서 자신의 "아래"에 있는 컴포넌트에만 영향을 미친다.
이벤트 처리하기
- React 엘리먼트에서 이벤트를 처리하는 방식은 DOM 엘리먼트에서 이벤트를 처리하는 방식과 매우 유사하다.
DOM 엘리먼트에서의 이벤트 처리와 React 이벤트 처리의 차이점
- React의 이벤트는 소문자 대신 camelCase를 사용한다.
- JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다.
- React에서는
false
를 반환해도 기본 동작을 방지할 수 없다. 반드시preventDefault
를 명시적으로 호출해야 한다. - React에서는 리스너를 추가하기 위해
addEventListener
를 호출할 필요가 없다. 대신 엘리먼트가 처음 렌더링될 때 리스너를 제공하면 된다. (클래스 방식 컴포넌트에서는 이벤트 핸들러를 클래스의 메서드로 만드는 것이 일반적인 패턴이다.)
- HTML
<button onclick="activateLasers()"> Activate Lasers </button>
- React
<button onClick={activateLasers}> Activate Lasers </button>
- HTML 에서 폼을 제출할 때 가지고 있는 기본 동작을 방지하기 위해
return false
를 사용할 수 있다. <form onsubmit="console.log('You clicked submit.'); return false"> <button type="submit">Submit</button> </form>
- React에서는 다음과 같이 작성할 수 있다.
- React는 W3C명세 에 따라 합성 이벤트를 정의하기 때문에 브라우저 호환성에 대해 걱정할 필요가 없다.
- React 이벤트는 브라우저 고유 이벤트와 정확히 동일하게 동작하지는 않는다. 합성 이벤트
function Form() { function handleSubmit(e) { e.preventDefault(); console.log('You clicked submit.'); } return ( <form onSubmit={handleSubmit}> <button type="submit">Submit</button> </form> ); }
조건부 렌더링
- React에서 조건부 렌더링은 JS에서의 조건 처리와 같이 동작한다.
if
나조건부 연산자
와 같은 JS 연산자를 현재 상태를 나타내는 엘리먼트를 만드는 데에 사용하면 React는 현재 상태에 맞게 UI를 업데이트 할 것이다.
논리 && 연산자로 if를 인라인으로 표현하기
- JS 논리 연산자
&&
을 사용하면 쉽게 엘리먼트를 조건부로 넣을 수 있다.
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
null을 이용해서 컴포넌트가 렌더링하는 것 막기
- 컴포넌트의 render 메서드로부터
null
을 반환하는 것은 생명주기 메서드 호출에 영향을 주지 않는다.
리스트와 Key
Key
- Key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다. key는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 한다.
- 대부분의 경우 데이터의 ID를 key로 사용한다.
- React는 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인한다.
- React는 휴리스틱 알고리즘에 의존하고 있기 때문에 휴리스틱이 기반하고 있는 가정에 부합하지 않는 경우 성능이 나빠질 수 있다.
- key는 반드시 변하지 않고, 예상 가능하며, 유일해야 한다. 변하는 key를 사용하면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state가 유실될 수 있다.
- key는 형제 사이에서만 고유한 값이어야 한다. (전체 범위에서 고유할 필요는 없다.
- key는 react에게 힌트를 제공할 뿐 컴포넌트로 전달하지는 않는다.
폼
- HTML 폼 엘리먼트는 폼 엘리먼트 자체가 내부 상태를 가지기 때문에, React의 다른 DOM 엘리먼트와 다르게 동작한다.
🔥제어 컴포넌트🔥
- HTML에서
<input>
,<textarea>
,<select>
와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트 한다. React에서는 변경할 수 있는 staet가 일반적으로 컴포넌트의 state 속성에 유지되며setState()
에 의해 업데이트된다. - React state를 신뢰 가능한 단일 출처 로 만들어 두 요소를 결합할 수 있다. 그러면 폼을 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어한다. 이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를 제어 컴포넌트 라고 한다.
- input의
value
어트리뷰트는 항상this.state.value
가 되고 React state는 신뢰 가능한 단일 출처가 된다. - input의 onChange에 setState를 넣어 사용자가 입력할 때 보여지는 값이 업데이트 된다.
- 제어 컴포넌트로 사용한다면, input의 값은 항상 React state에 의해 결정된다. 이렇게 하게되면 다른 UI 엘리먼트에 input의 값을 전달하거나 다른 이벤트 핸들러에서 값을 재설정할 수 있다.
textarea 태그
- HTML 에서
<textarea>
엘리먼트는 텍스트를 자식으로 정의한다.<textarea> Hello there, this is some text in a text area </textarea>
- React에서는 value 어트리뷰트를 대신 사용한다.
select 태그
- HTML 에서
<select>
는 드롭 다운 목록을 만든다. selected 옵션이 있다. - React에서는 selected 어트리뷰트를 사용하는 대신 최상단 select 태그에 value 어트리뷰트를 사용한다. 한 곳에서 업데이트만 하면되기 때문에 제어 컴포넌트에서 사용하기 더 편리하다.
<select value={this.state.value} onChange={this.handleChange}> <option value="grapefruit">Grapefruit</option> <option value="lime">Lime</option> <option value="coconut">Coconut</option> <option value="mango">Mango</option> </select>
- select 태그에 multiple 옵션을 허용한다면 value 어트리뷰트에 배열을 전달할 수 있다.
<select multiple={true} value={['B', 'C']}>
다중 입력 제어하기 (다중 input)
- 여러
input
엘리먼트를 제어해야 할 때, 각 엘리먼트에name
어트리뷰트를 추가하고event.target.name
값을 통해 핸들러가 어떤 작업을 할 지 선택할 수 있게 해준다.
'React' 카테고리의 다른 글
[React] 공식문서 HOOK 4 ~ 8정리 (0) | 2022.09.06 |
---|---|
[React] 공식문서 주요 개념 10 ~ 12, HOOK 1 ~ 3정리 (0) | 2022.09.05 |
[Router] React Router (0) | 2022.08.21 |
[Redux] Redux 기초 (0) | 2022.07.31 |
[Webpack] creat-react-app 없이 react 시작하기 (0) | 2022.06.14 |