리액트 공식문서를 읽어보며 정리를 해보았다.
공식문서 : https://ko.reactjs.org/docs/hello-world.html
전체를 정리하면서 알아야 할 것 같은 개념들 위주로 정리하였다.
ReactDOM.render()
로 부터 React가 시작된다.const element = <h1>Hello, World</h1>
이 태그는 문자열도 html도 아니다.
JSX 라 부르는 JS를 확장한 문법이며, React element
를 생성하기 위해 사용된다.
React는 별도의 파일에 마크업과 로직을 넣어 기술을 분리하는 대신, 둘다 포함하는 컴포넌트
로 관심사를 분리한다.
JSX 사용이 필수는 아니지만, JS 코드 안에서 UI 작업을 할때 시각적으로 더 도움이 된다.
React의 경고 메시지를 사용할 수 있다.
const element = <h1>Hello,{name}</h1>
JSX의 중괄호 안에는 유효한 모든 JS 표현식을 넣을 수 있다.
Babel은 JSX를 React.createElement()
호출로 컴파일한다.
const element = <h1 className="hi">Hello, World</h1>;
const element = React.createElement("h1", { className: "hi" }, "Hello, World");
위의 두 예시는 동일한 코드이다.
React.createElement
는 버그가 없는 코드 작성에 도움이 되며, 실행시 다음과 같은 객체를 생성하게 된다.
const element = {
type: "h1",
props: {
className: "hi",
children: "Hello, Word",
},
};
이 객체를 React 엘리먼트
라고 하며, 화면에서 보고 싶은 것을 나타내는 표현이다.
React는 이 객체를 읽어서, DOM을 구성하고, 최신 상태로 유지한다.
엘리먼트는 React 앱의 가장 작은 단위이다.
엘리먼트는 화면에 표시할 내용을 기술한다.
브라우저의 DOM 엘리먼트와는 달리 React 엘리먼트는 일반 객체 이기 때문에 쉽게 생성할 수 있다.
React DOM은 React 엘리먼트와 일치하도록 DOM을 업데이트한다.
<div id="root"></div>
root div 태그 안에 들어가는 모든 엘리먼트를 React DOM에서 관리한다.***
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(<h1>Hello,World<h1/>);
React 엘리먼트
를 렌더링하기 위해서는 우선 DOM 엘리먼트
를 ReactDOM.createRoot()
에게 전달한 다음,
React 엘리먼트
를 root.render()
에게 전달해야한다.
React Element는 불변격체이다 ?
엘리먼트는 특정시점의 UI이기 때문에 생성한 이후에는 해달 엘리먼트의 자식이나 속성을 변경할 수 없다.
시계 어플을 만들어 보겠다.
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);
1초마다 root.render()
를 호출해서, 다시 그리도록 만들었지만, 개발자 도구를 켜서 확인하면, 텍스트만 변경된다
=> 다음장에서..
컴포넌트를 통해 UI를 재사용 가능한 개별적인 조각으로 나눠 사용가능하다.
컴포넌트는 JS 함수와 유사하다. props라는 매개변수를 받아서, 화면에 어떻게 표시될는지를 기술하는 React 엘리먼트 반환
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
몇가지 규칙을 알아보자
컴포넌트의 이름은 항상 대문자로 시작해야 한다.
props는 읽기 전용이기 때문에 props를 수정해서는 안된다.
이러한 규칙을 위반하지 않기 위해 state라는 정의가 생겨나게 된다.
state는 props와 유사하지만, 비공개이며 컴포넌트에 의해 완전히 제어된다.
클래스형 형태에서 state를 이해해보자
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
this.props.date.toLocaleTimeString()
의 props를 state
로 바꿔준다.this.state
를 지정하는 constructor
를 추가한다.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.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Clock />);
컴포넌트 출력물이 DOM에 렌더링 된 후에 실행된다.
생성된 DOM이 삭제될 때마다 실행된다.
아래의 예제는 타이머가 작동하고, 사용되지 않을 때 타이머를 없애는 작업이다
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/>
이 root.render()
로 전달되었을 떄, React는 <Clock/>
의 constructor()
를 호출한다.
<Clock/>
은 현재 시각이 포함된 객체로 state
를 초기화한다.
React는 <Clock/>
의 render()
메서드를 호출하여 화면에 표시해야할 내용을 알게 된다.
다음 React는 <Clock/>
의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트한다.
<Clock/>
출력값이 DOM에 삽입되면, componentDidMount()
를 호출하고, 매초마다 tick()
메소드를 실행하도록 브라우저에 요청
브라우저가 tick()
메소드를 호출하고, 그 안에서 <Clock/>
는 setState()
에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트 진행
setState()
호출을 통해 React는 state
가 변경된 것을 인지하고, 화면에 표시될 내용을 render()
메서드를 통해 다시 호출
render()
메서드 안의 this.state.date
가 달라지고, 렌더링 출력값은 업데이트된다. <Clock/>
이 DOM으로 부터 삭제가 된다면 componentWillUnmount()
메서드 호출 State 는 직접 수정하지 않고, setState를 이용한다.
this.state를 지정할 수 있는 유일한 공간은 constructor 이다.
this.props와 this.state가 비동기적으로 업데이트가 될 수도 있기 때문에, state를 계산할 때 해당 값에 의존하면 안된다.
기존값에 의존적으로 업데이트를 하고 싶다면 setState 내부에 함수를 넣고 기존 state를 배개변수로 받아와서 사용한다.
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
병합은 얕게 이루어지기 때문에, 변수를 독립적으로 업데이트할 수 있다.
React 엘리먼트에서 이벤트를 처리하는 방식은 DOM 엘리먼트에서 이벤트를 처리하는 방식과 유사하다.
HTML에서는 문자열을 전달했지만, React에서는 핸들러를 전달한다.
onclick ="function()"
onClick = {function}
onClick = {this.handleClick}
과 같이 ()
없이 메서드를 참조할 경우 바인딩이 필요하다.원하는 동작을 캡슐화하는 컴포넌트를 만들 수 있어 상태에 따라 컴포넌트 몇개만 렌더링 가능
컴포넌트 분리하기에 좋다
render()
메서드로 null을 반환하는 것은 생명주기에 영향을 주지 않고, componentDidUpdate
는 계속해서 호출이 된다.