JSX In Depth

후훗♫·2020년 2월 22일
0

React 공식홈페이지에 'JSX In Depth' 라는 문서가 있다.
어찌보면 자잘(?)한 부분일 수 있지만,
실제로 과제를 하면서 미리 알았으면 훨씬 도움이 될 법한 내용이었다.

0. 시작에 앞서..

JSX는 React.createElement를 위한 Syntactic sugar이다.

//아래와 같이 JSX를 쓰면,
<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

//이렇게 변환된다.
React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

즉, JSX는 Babel을 통해 React.createElement로 컴파일된다.

React를 접하면, Babel, Webpack이란 단어가 들린다...

Babel은 트랜스파일러(Transpiler)이다.
Babel은 ES6문법을 ES5문법으로 변환해주고, JSX를 JavaScript로 변환해준다.
(브라우저에서 ES6 문법을 지원하지 않을 수 있으므로, ES5 문법으로 변환이 필요하다!)

Babel에도 여러 종류가 있다. (....ㅜㅜ)
@babel/core는 babel의 필수 라이브러리이고,
@babel/preset-react는 JSX를 JavaScript로 변환해주고,
@babel/preset-env는 ES6를 ES5 문법으로 변환해준다.

Webpack은 모듈 번들러(Module bundler) 이다.
개발을 하다보면 여러 라이브러리(모듈)를 사용하는데,
라이브러리 간 의존성을 고려하여 하나의 파일로 묶어주는(번들러..) 역할을 한다.

Webpack에는 Entry, Output, Loader, Plugin이란 개념이 있다.

Entry는 의존성 그래프의 시작점으로,
엔트리를 통해 필요한 모듈을 로딩하고 하나의 파일로 묶는다. (여러 개 일 수도 있다.)
(Index-App-Game component 순서로 연결되어있다면, Index가 Entry이다.)

Output은 번들된 결과이다. (최종 output 이라 이해했다.)

Loader는 Webpack이 이해하는 JavaScript, Json 외에
다른 Type(img, stylesheet 등)의 파일을 이해할 수 있도록 변환해준다.
(그래서 react 쓸때도 babel-loader, css-loader, style-loader 등이 필요하다)

Plugin은 일종의 옵션 처리이다.
난독화(Uglify) 여부, output에 주석 포함 여부, output의 크기나 갯수 설정 등을 할 수 있다.

(또 시작이 길어졌다...)

React에서는 webpack, babel 설정이 필수이지만,
create-react-app이 있어 이 것들을 배우지 않아도 react를 사용할 수 있었다..
(감사합니다..)

1. Specifying The React Element Type

1) React Must Be in Scope

import React from 'react'; // 👉 component를 작성할 때 항상 이렇게 시작했다.
import CustomButton from './CustomButton';

function WarningButton() {
  // 👉이렇게 바뀐다! 
  // return React.createElement(CustomButton, {color: 'red'}, null);
  return <CustomButton color="red" />;
}

component를 쓸 때 가장 첫 줄은 import React from 'react'
component 파일 안에서 import된 React를 직접적으로 쓰지 않아도 넣어야 하는 건,
JSX가 babel을 통해 React.createElement컴파일 되기 때문이다.

.(점)실행(Dot notation)으로 createElement라는 method가 실행되어야 하는데,
.(점) 앞의 'React'가 없는건 이상한(?) 상황이다.
(그냥 버릇처럼 쓰고 있었던 것에 반성한다...ㅎㅎ)

2) Using Dot Notation for JSX Type

component는 점 실행(Dot Notation)으로도 사용 가능하다.

import React from 'react';

const MyComponents = {
  DatePicker: function DatePicker(props) {
    return <div>Imagine a {props.color} datepicker here.</div>;
  }
}

function BlueDatePicker() {
  return <MyComponents.DatePicker color="blue" />;
} //👉 MyComponents 객체의 DatePicker Method의 출력값이 반영된다.

3) User-Defined Components Must Be Capitalized

component는 무조건 대문자시작한다.
div, h1 처럼 소문자로 시작하면 tag로 인식한다.

import React from 'react';

//👉 원래 의도대로 <div>로 잘 인식한다.
function hello(props) {
  return <div>Hello {props.toWhat}</div>; 
}

// Wrong!!
function HelloWorld() {
  return <hello toWhat="World" />; //👉<hello>라는 tag로 인식!
}

// Good!!
function HelloWorld() {
  return <Hello toWhat="World" />; //👉<Hello>라는 component로 인식!
}

4) Choosing the Type at Runtime

React에서는 일반적인 표현식은 사용할 수 없다.

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

//Wrong!!
function Story(props) {
  // 👉 이런식으로 쓰면 쓸 수 없는 표현이라고 빨간줄이 나온다..
  return <components[props.storyType] story={props.story} />;
}

//Good!!
function Story(props) {
  // 👉 <대문자>로 시작되는 변수에 담아 사용한다.
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}

위의 예시에서 "const components"를 대문자로 사용하면 가능할까 궁금했다.
(ex, return <Components[props.storyType] story={props.story})
결론은 VS code에서 써보면, 'parsing error"라고 나온다.ㅎㅎ

2. Props in JSX

1) JavaScript Expressions as Props

자바스크립트 표현식(expression)중괄호({})로 감싸서 표현하면,
component에 prop로 전달할 수 있다.

// 👉 props foo에 10이란 값이 전달된다.
<MyComponent foo={1 + 2 + 3 + 4} /> 

if문for loop 등은 표현식이 아니기 때문에 JSX에서 사용할 수 없다.

function NumberDescriber(props) {
  let description;
  if (props.number % 2 == 0) {
    description = <strong>even</strong>;
  } else {
    description = <i>odd</i>;
  }
  return <div>{props.number} is an {description} number</div>;
}

Javascript에서는 표현식(expression)과 문(statment)로 구분된다.
표현식(expression)은 값을 만든다. 그래서 함수의 인자로 사용이 가능하다

문(statement)은 좀 이해가 안갔는데, 그나마 정리된 블로그가 있었다. 출처
statement란 특정 액션을 수행하는 코드로, 사실상 코드 모두가 statement다.
statement는 값을 도출할 수 있는데, 이는 Expression Statement라 한다.
반면 if statement나 for statement처럼 그 자체로는 값을 도출하지 않을 수도 있다.
이렇게 값을 도출하지 않는 statement는 함수의 인자로 들어갈 수 없다.
결국 모든 expression은 statement이지만,
모든 statement가 expression은 아니다.

즉, 값이 있는 표현식(expression)만 props로 사용이 가능하다는 것 아닐까?

2) String Literals

string은 그대로 props로 전달된다. ({}를 쓰지 않아도 된다!)

// 👉 모두 동일한 props 값이 전달된다.
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />
  
//👉 &lt;와 같은 이스케이프(escape) 코드도 props로 잘 전달된다.
// 아래도 역시 동일한 props 값이 전달된다.
<MyComponent message="&lt;3" />
<MyComponent message={'<3'} />

* 출처: 생활코딩
이스케이핑(escaping)이란 어떤 특정 문자를 쓰여진 그대로가 아닌 다른 의미로 사용하는 것을 말한다.
HTML에서 <br />태그를 줄바꿈의 용도이다.
하지만 "<br />"의 형태로 화면에 표시하기 위해서는 어떻게 해야할까?

//👉 이스케이프 코드를 활용한다!
// &lt; => "<" 
// &gt; => ">"
<html>
    <body>
        &lt;br /&gt;은 줄바꿈을 의미하는 태그입니다. 
    </body>
</html>

ex) 스페이스 이스케이코드 : &nbsp; &ensp; &emsp; 
    (순서대로 스페이스 간격이 길어진다.)

3) Props Default to “True”

Props의 기본값은 true이다.

// 👉 동일한 내용이다!
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />

그러나, 이 방법은 ES6 object shorthand과 헷갈리기 때문에 사용하지 않는게 좋다.
React 문서는 props {foo} 는 {foo: true} 가 아닌 {foo: foo} 와 동일하다고 설명한다.

ES6 object shorthand는 객체에서 key와 value가 같을 경우, 한개의 값으로 표현 가능하다. * 출처: MDN

// 👉 동일한 내용이다!
let o = { 
  a: a,
  b: b,
  c: c
};

let o = { a, b, c };

4) Spread Attributes

props로 전달할 때 spread 연산자를 사용할 수도 있다.

function App() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />; // 👉 spread 연산자!
}

이 방법은 필요하지 않은 props까지 전달될 수 있고,
DOM에 유효하지 않은 HTML attribute까지 전달 될 수 있다.
React 문서에서는 꼭 필요할 때만 사용하라고 추천한다.

3. Children in JSX

component는 opening tag, closing tag가 있다.
그 사이에 들어가는 내용은 props.children로 전달된다.

1) String Literals

props.children을 string으로 전달 할 수 있다.

<MyComponent> // 👉 opening tag
  Hello world! // 👉 Mycomponent의 props.children안에 들어가있다.
</MyComponent> // 👉 closing tag

JSX는 line의 시작과 끝의 공백문자, tag의 개행문자, 빈줄 모두를 삭제하므로,
아래는 모두 동일한 내용으로 반영된다!

<div>Hello World</div>

<div>
  Hello World
</div>

<div>
  Hello
  World
</div>

<div>

  Hello World
</div>

2) JSX Children

JSX는 JSX 자체를 children으로도 활용 가능하다.
이 방법을 사용하면 component중복으로 사용 가능하다.

<MyContainer>
  <MyFirstComponent />
  <MySecondComponent />
</MyContainer>

React compoenent는 array각 value를 return한다.
(단, key 값을 넣어줘야 한다.)

render() {
  // No need to wrap list items in an extra element!
  return [
    // Don't forget the keys :)
    <li key="A">First item</li>,
    <li key="B">Second item</li>,
    <li key="C">Third item</li>,
  ];
}

그래서 map, filter method를 react component의 return 값으로 바로 적용하면,
map, filter가 실행되어 return된 배열의 각 value가 component의 return 값으로 적용된다.
단, react는 각 요소 별 key값을 요청하기 때문에,
map, filter의 콜백함수 return 값에 key를 설정해줘야 한다.
(key값은 unique 해야한다!)

3) JavaScript Expressions as Children

{}안에 자바스크립트 표현식을 쓰면 props.children으로 전달할 수 있다.

function TodoList() {
  const todos 
    = ['finish doc', 'submit pr', 'nag dan to review'];
  
  return (
    <ul>
      {todos.map((message) => {
        return <Item key={message} message={message} />
      })} // 👉 위에서 언급한 map! key값을 넣어줘야한다!
    </ul>
  );
}

4) Functions as Children

함수를 props.children으로도 전달 할 수 있다!

// Calls the children callback numTimes to produce a repeated component
function Repeat(props) {
  let items = [];
  for (let i = 0; i < props.numTimes; i++) {
    items.push(props.children(i)); // 👉 props.children은 함수이므로, i를 인자로 받아 실행된 값이 배열 item으로 push된다.
  }
  return <div>{items}</div>;
}

function ListOfTenThings() {
  return (
    <Repeat numTimes={10}>
      {(index) => <div key={index}>This is item {index} in the list</div>} // 👉 함수가 props.children으로 전달된다.
    </Repeat>
  );
}

5) Booleans, Null, and Undefined Are Ignored

false, null, undefined, truerendering 되지 않는다.

//모두 동일한 내용이다!
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>

조건부로 React 요소를 rendering 할때 유용하다.

<div>
  {showHeader && <Header />} //👉 showHeader가 true일 때만 rendering 된다!
  <Content />
</div>

단, 0 숫자 같은 falsy값rendering된다.

//wrong!!
<div>
  {props.messages.length && // 👉빈 배열이면 0이 같이 나온다!
    <MessageList messages={props.messages} /> 
  }
</div>

//Good!!
<div>
  {props.messages.length > 0 && // 👉 어떤 조건이든 boolean값으로 나오게 한다!
    <MessageList messages={props.messages} />
  }
</div>

false, null, undefined, true 를 rendering하려면 string으로 변환해야한다.

// 👉 My JavaScript variable is undefined로 나온다!
<div>
  My JavaScript variable is {String(undefined)}.
</div>

4. 참고

profile
꾸준히, 끄적끄적 해볼게요 :)

0개의 댓글