React 공식홈페이지에 'JSX In Depth' 라는 문서가 있다.
어찌보면 자잘(?)한 부분일 수 있지만,
실제로 과제를 하면서 미리 알았으면 훨씬 도움이 될 법한 내용이었다.
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를 사용할 수 있었다..
(감사합니다..)
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'가 없는건 이상한(?) 상황이다.
(그냥 버릇처럼 쓰고 있었던 것에 반성한다...ㅎㅎ)
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의 출력값이 반영된다.
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로 인식!
}
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"라고 나온다.ㅎㅎ
자바스크립트 표현식(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로 사용이 가능하다는 것 아닐까?
string
은 그대로 props로 전달된다. ({}를 쓰지 않아도 된다!)
// 👉 모두 동일한 props 값이 전달된다.
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />
//👉 <와 같은 이스케이프(escape) 코드도 props로 잘 전달된다.
// 아래도 역시 동일한 props 값이 전달된다.
<MyComponent message="<3" />
<MyComponent message={'<3'} />
* 출처: 생활코딩
이스케이핑(escaping)이란 어떤 특정 문자를 쓰여진 그대로가 아닌 다른 의미로 사용하는 것을 말한다.
HTML에서 <br />태그를 줄바꿈의 용도이다.
하지만 "<br />"의 형태로 화면에 표시하기 위해서는 어떻게 해야할까?
//👉 이스케이프 코드를 활용한다!
// < => "<"
// > => ">"
<html>
<body>
<br />은 줄바꿈을 의미하는 태그입니다.
</body>
</html>
ex) 스페이스 이스케이코드 :    
(순서대로 스페이스 간격이 길어진다.)
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 };
props로 전달할 때 spread 연산자
를 사용할 수도 있다.
function App() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />; // 👉 spread 연산자!
}
이 방법은 필요하지 않은 props까지 전달될 수 있고,
DOM에 유효하지 않은 HTML attribute까지 전달 될 수 있다.
React 문서에서는 꼭 필요할 때만 사용하라고 추천한다.
component는 opening tag, closing tag가 있다.
그 사이에 들어가는 내용은 props.children
로 전달된다.
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>
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 해야한다!)
{}
안에 자바스크립트 표현식
을 쓰면 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>
);
}
함수
를 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>
);
}
false
, null
, undefined
, true
는 rendering 되지 않는다.
//모두 동일한 내용이다!
<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>