React

[React] 공식문서 주요 개념 1 ~ 9 정리

Alexim 2022. 9. 4. 17:34

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 />);
  1. <Clock /> 엘리먼트가 ReactDOM.render()로 전달되었을 때 React는 Clock 컴포넌트의 constructor를 호출한다. Clock 에서는 this.state를 초기화한다.
  2. React에서는 Clock 컴포넌트의 render() 메서드를 호출한다. 이를 통해 React는 화면에 표시되어야 할 내용을 알게 된다. 그 다음 React는 Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트 한다.
  3. Clock 출력값이 DOM에 삽입되면, React는 componentDidMount() 생명주기 메서드를 호출한다. 그 안에서 Clock 컴포넌트는 매초 컴포넌트의 tick() 메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요청한다.
  4. 매초 브라우저가 tick() 메서드를 호출한다. 그 안에서 Clock 컴포넌트는 setState()에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트를 진행한다.
    • setState() 호출 덕분에 React는 state가 변경된 것을 인지하고 화면에 표시될 내용을 알아내기 위해 render() 메서드를 다시 호출한다. 이 때 render() 메서드 안의 this.state.date가 달라지고 렌더링 출력값은 업데이트된 시각을 포함한다. React는 이에 따라 DOM을 업데이트 한다.
  5. 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 이벤트 처리의 차이점

  1. React의 이벤트는 소문자 대신 camelCase를 사용한다.
  2. JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다.
  3. React에서는 false를 반환해도 기본 동작을 방지할 수 없다. 반드시 preventDefault를 명시적으로 호출해야 한다.
  4. 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 값을 통해 핸들러가 어떤 작업을 할 지 선택할 수 있게 해준다.