React.js 공식문서 정리 - Main Components
들어가며
- 개발자에게 가장 중요한 습관은 문서를 읽는 습관이라고 생각합니다.
- 이 글은 리액트 공식 문서를 정독하며 내용을 정리하기 위해 작성하게 되었습니다.
- 모든 내용을 정리하기 보다는, 모르는 내용이나 기억하고 싶은 내용 위주로 정리합니다.
- 공식 문서의 주소는 https://reactjs.org/docs/hello-world.html 입니다.
MAIN CONCEPTS
1. Hello World
- ReactDOM.render로부터 리액트 앱이 시작됨.
- 첫번째는 보낼 react element, 두 번째 인자는 html문서로부터 id = root인 태그를 가져옴.
2. Introducing JSX
- JSX는 JS의 syntax extension이다.
- JSX 자체는 문법(syntax)의 이름이고, JSX의 생성물은 리액트 엘리먼트이다.
- 컴포넌트는 관심사를 분리한다.
- JSX안에는 JS 표현식을 넣을 수 있다. 표현식은 쉽게 말해 const x = something에 들어올 수 있는 something의 모든 것. 값이다 (JS는 일급객체인 함수 등도 포함된다.)
- JSX도 컴파일 후 JS 객체로 인식이 되므로, Expression이다.
- attribute로 문자열 리터럴, 객체를 줄 수 있다. 문자열을 줄 때는 따옴표, 객체를 줄 때는 중괄호 이용
- React DOM은 JSX의 값을 렌더링 전에 이스케이프한다.(안전하다)
3. Rendering Elements
- 리액트 엘리먼트는 불변객체이다.
- setInterval로 부른 예시는 특별한 경우로, 보통과는 다르다(전체를 리렌더링)
- 개발자도구에서 확인하면 시간만 딱업데이트 되는데, Virtual DOM 덕분인 듯하다.
4. Components and Props
- React element는 DOM 태그만이아니라 사용자 정의 컴포넌트로도 나타낼 수 있다.
- 컴포넌트란 말 자체가 순수 DOM태그와 구분하기 위한 말로 존재한다고 생각해도 될 듯 하다. 결국에는 DOM태그가 필요하다.
- React는 React Element의 1) JSX 어트리뷰트와 2)자식 을 해당 컴포넌트에 단일 객체로 전달한다.
- 사용자 컴포넌트는 항상 대문자로 시작한다.
- 컴포넌트는 자신이 어디서 렌더링 되는지 모른다. 네이밍 시 context가 아닌 컴포넌트 자체의 관점에서!
- 컴포넌트를 추출함으로써 코드를 단순화시킬 수 있다.
- 모든 React Component는 props에 대해 순수함수처럼 동작해야 한다. 읽기전용!
5. State and Lifecycle
- 클래스 컴포넌트 사용 시, 생성자 내에서 우선 super(props)
- 이후 this.state를 객체로 정의, 프로퍼티 키로 state의 이름들을 설정한다.
- 컴포넌트가 처음 DOM에 렌더링(DOM트리에 등록된다고 생각하자) 되는 것을 마운트라고 한다.
- 마운트 직후, 언마운트 직후 생명주기 관련 메소드를 쓸 수 있다.
While this.props is set up by React itself and this.state has a special meaning, you are free to add additional fields to the class manually if you need to store something that doesn’t participate in the data flow (like a timer ID).
- 모든 변수를 state로 관리할 필요는 없다. 필드로 관리해도 된다.
- 예제에서는 componentDidMount에 setInterval을 부여해서 tick을 주기적으로 호출하고, tick을 state로 선언해서 주기적으로 갱신하는 방법을 쓴다.
- 실행순서가 매우 중요한데, 생성자 이후 바로 render가 최초로 한 번 실행이 된다. 그리고 render에 의해 컴포넌트가 DOM에 등록되는 마운트라는 이벤트가 발생하고, 이후 생명주기 함수들에 의해 연쇄적으로 동작한다.
- State를 직접수정하지말고, 항상 setState를 이용한다.
- State업데이트는 비동기적으로 일어날 수 있다. 성능을 위해 여러 setState호출이 단일 업데이트로 한꺼번에 처리될 수 있다. setState시 기존의 값에 의존적으로 만들면 안된다. 기존값에 의존적으로 update하고 싶다면 setState 내부에 함수를 넣고 기존 state를 매개변수로 받아온다.
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
- setState시 기존 state가 객체였으면 특정 프로퍼티만 업데이트할 수 있다.
6. Handling Events
- HTML에서는 onclick = "function()" 느낌이라면(즉, HTML에서는 따옴표 내에 함수를 호출하는 JS코드를 적어서 전달한다) React에서는 onclick = {function}으로 핸들러를 전달한다.
- 리액트에서는 return flase 같은 방식으로 prevendDefault하는 것이 불가능하다. 즉, 핸들러 호출 후 핸들러 내에서 처리해주어야 한다.
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>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
- 위 예시 코드를 보면, button에 this.handleClick이 핸들러로 할당되어있다.
- setState가 저런 방식으로 state를 개선하는 이유는 setStateAPI때문이다. https://reactjs.org/docs/react-component.html 를 참조하자. this.setState 안에 있는 부분은 updater인데, state랑 props를 입력으로 받고 새로운 state를 리턴한다. 이러한 방식을 통해 객체불변성이 유지된다.
- 다만 이때, handleClick은 this(this.setState)를 기반으로 동작한다. this.handleClick이 this를 잃어버리지 않도록 바인딩을 해줘야한다. 바인딩 방식은 .bind(this)를 재할당하면 된다.
- 컴포넌트 내의 메소드에서 this를 기반으로 한 메소드가 있을 경우, 외부에서 그 함수를 호출할 때, 생성자에서 this binding을 해줘야 한다. 외부에서 메소드 호출시 this가 전역 객체로 할당되는 것을 막기 위함이다. 사용법은 메소드 = 메소드.bind(this) 정도.
- 퍼블릭 클래스 필드를 사용하는 것도 하나의 방법이다.
- 화살표함수의 리턴에 호출을 할 수도 있는데, 바인딩이나 클래스 필드 문법이 정석이다.
- 핸들러에 함수를 넣어줄 때 bind를 해서 넘겨줘도 된다, 이 때 첫 인자는 this
- onClick등으로 컴포넌트에 핸들링 메소드를 넣어줄 때 리액트 이벤트도 마지막 인자로 전달된다.
- this방식에서는 자동으로 전달되지만 화살표함수에서는 명시적으로 이벤트를 전달해주어야 한다.
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
7. Conditional Rendering
- 조건문을 통해 컴포넌트를 다르게 리턴할 수 있다.
- 버튼에 조건부로 다른 컴포넌트를 할당할 수 있다.
- &&연산을 활용할 수 있다.
- falsy && 컴포넌트를 하면 컴포넌는 무시되어도, fasly는 반환된다.
- state를 const에서 상수로 받아오고, 조건연산자를 통해 렌더링에 변화를 줄 수 있다.
- 렌더링을 막고 싶을 때는 조건부로 return null을 하자
8. List and Keys
- ul태그 안에 li태그의 배열을 넣어도 잘 작동한다.
- li 안에 어트리뷰트로 key값을 주어야한다.
- item 자체에 id를 부여한 상태면 key를 정하기 편하다. 인덱스를 사용하는 것은 지양하도록 하자.
- key는 무조건 li컴포넌트에게 주는 것이 아니라 map의 리턴부에서 주어야 한다.
- 키는 전체범위에서 고유할 필요는 없고, 리턴부 안에서만 고유하면 된다.
- props로 전달받은 key라는 어트리뷰트는 읽을 수 없다.
- 가독성을 위해서 인라인으로 긴 JS표현식을 리턴하는 짓은 하지말자. 컴포넌트를 추출하자.
- 컴포넌트 추출할 때 한 번에 여러 파일 만들면 너무 머리아프니까, 일단 같은 파일 안에서 추출하고, 그걸 다른데로 보내는 방식으로 해보자.
9. Forms
- In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. -- submit이벤트 발생 시에만 입력값을 기준으로 데이터 처리
- In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState() -- state라는 property에 보존, setState로 개선
- We can combine the two by making the React state be the “single source of truth”. Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a “controlled component
- 아래 예시를 보자.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
7. Conditional Rendering
7. 조건부 렌더링
7. 조건부 렌더링
- Single Source of Truth 원칙에 의해 데이터는 this.state.value에 의해서만 관리되며, NameForm이라는 엘리먼트를 윗 문장의 the react component (that renders...)로 생각할 수 있고, input component가 controlled component가 된다. controlled 라는 수식어가 붙는 이유는 handleChange에 의해 NameForm에게 항상 지배받기 때문이 아닐까하고 예측해본다.
- HTML과 달리 React에서 textArea를 사용할 때는 value로 input과 비슷한 방식으로 정의한다.
- 드롭다운 메뉴를 만드는 select 컴포넌트의 경우 value값에 현재 this.state.value를 준다. 자식 태그로 옵션 태그를 준다.
- 다중 input을 제어할 때 onchange메소드를 하나로 할 수 있다. 단, input 컴포넌트에 name어트리뷰트를 부여해서 이름을 구분하고, handleChange에서는 (암묵적으로 할당되는) event를 입력을 살려서 타겟을 받아오고, 그걸 기반으로 setState해줄 수 있다. 애초에 state를 단일 객체(state={key:value, key:value ...})로 설계를 해야한다.
10. Lifting State Up
- fieldset은 박스를, legend는 filedset의 제목을 나타내준다.
- 예시를 따라가다보면, input을 두 개로 늘렸을 때 값이 동기화가 안되는 문제가 발생.
- 컴포넌트의 가장 가까운 공통조상까지 state를 끌어올림으로써 문제를 해결한다.
- 결국 요점은 상위 컴포넌트에서 state를 잡고, 하위컴포넌트에는 상위 컴포넌트의 setstate를 뿌려주라는 얘기이다. 동시에 하위 컴포넌트에서는 props로 읽어온다.
- 상위 컴포넌트의 state가 바뀜에 따라 리렌더링 되는 것이 포인트.
- 끌어올리기보다 하향식 데이터 흐름에 기대라.
<!DOCTYPE html> <body> <script src="lib/jquery.min.js"></script> <script src="background.js"></script>
11. Composition vs Inheritance
- container 나 wrapper 등을 추출한다고 할 때, props의 children을 그냥 그대로 흘려준다. 기존에는 Container를 정의할 때 Styled components와 연동하면 아래 계속 컨테이너도 쌓이고 style도 쌓였으나... 아예 다른 파일로 분리를 하고, 깔끔하게 태그만 가져와서 쓰자.
- 구체적인 컴포넌트(제목, 특수내용등을 가지고 있는)가 일반적인 컴포넌트를 렌더링하는 방향으로 작성한다.
- 상속은 extends를 이용하는 방법일텐데.. props로 흘려내려주는 게 본질적인 리액트의 특성이기 때문에 딱히 상속을 할 이유가 없다.
12. Thinking in React
1단계
- 단일 책임 원칙을 지킬 것 - 하나의 컴포넌트는 하나의 일만
2단계
- Don’t use state at all to build this static version.
- props vs. state: state는 일반 자바스크립트 객체, 컴포넌트 안에서 관리됨. props는 함수 매개변수처럼 다른 컴포넌트에 전달됨.
3단계
- 중복배제원칙을 생각하라. state를 최소한으로 선언할 것.(Todolist 아이템카운트 예)
- props를 받아서 쓰는 것이라면, 시간이 지나도 불변한다면, 다른 state나 props로 계산이 가능하다면 state로 선언하지 말자.
4단계
- State는 어디에 있어야 하는가 ? -> State를 가지고 렌더링하는 컴포넌트들의 최소 공통조상에 있거나, 더 위에 있어야 한다.
5단계
- 상방향의 데이터 흐름을 만들 때는 콜백을 넘기자.
마치며
공식문서를 차근차근 읽으면서 리액트의 컨벤션도 자연스럽게 습득이 되는 것 같다. 원래 ADVANCED GUIDES에 대한 내용도 하나의 글로 적으려고 했는데.. 글이 너무 길어질 것 같은 관계로 다음 편에 적도록 해야겠다.
'Study Log' 카테고리의 다른 글
브라우저 공부 (0) | 2021.11.20 |
---|---|
React.js 공식문서 정리 - Hook (0) | 2021.11.18 |
React.js 공식문서 정리 - Advanced Guides (0) | 2021.11.17 |
Event (0) | 2021.11.09 |
JavaScript export와 export default 차이 (0) | 2021.11.09 |
댓글
이 글 공유하기
다른 글
-
React.js 공식문서 정리 - Hook
React.js 공식문서 정리 - Hook
2021.11.18 -
React.js 공식문서 정리 - Advanced Guides
React.js 공식문서 정리 - Advanced Guides
2021.11.17 -
Event
Event
2021.11.09 -
JavaScript export와 export default 차이
JavaScript export와 export default 차이
2021.11.09