김태희(로토/Roto) [Travel Systems CX]
Coupang Web Developer
Coupang
Bali 팀
김태희(로토/roto)
// es5 style
var YourComponent = React.createClass({
render: function() {
return (
<div>Hi?</div>
);
}
});
// es6 style
class YourComponent extends React.Component {
render() {
return (
<div>Hi!</div>
);
}
}
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(<HelloMessage name="John" />, mountNode);
"use strict";
var HelloMessage = React.createClass({
displayName: "HelloMessage",
render: function render() {
return React.createElement(
"div",
null,
"Hello ",
this.props.name
);
}
});
ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
Transpiling
// 자주하는 실수
<br>
<img src="bali.png">
// 올바른 예
<br/>
<img src="bali.png"/>
// 잘못된 예
<table cellpadding="5">
<tr rowspan="2">
...
</tr>
</table>
// 올바른 예
<table cellPadding="5">
<tr rowSpan="2">
...
</tr>
</table>
// 틀린 예
return (
<div>안녕</div>
<div>세상</div>
);
// 올바른 예
return (
<div>
<div>안녕</div>
<div>세상</div>
</div>
);
최상위에는 단일 Root Node가 있어야 한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React</title>
<script src="https://fb.me/react-15.2.1.js"></script>
<script src="https://fb.me/react-dom-15.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
// 이곳에 코드를 작성합니다.
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React</title>
<script src="https://fb.me/react-15.2.1.js"></script>
<script src="https://fb.me/react-dom-15.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
// HelloWorld Component 정의
var HelloWorld = React.createClass({
// Component는 render 함수를 기준으로 화면에 그려진다.
render: function () {
return (
<div>Hello World!!!</div>
);
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React</title>
<script src="https://fb.me/react-15.2.1.js"></script>
<script src="https://fb.me/react-dom-15.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
// HelloWorld Component 정의
var HelloWorld = React.createClass({
render: function () {
return (
<div>Hello World!!!</div>
);
}
});
// HelloWorld Comopnent 마운팅하기
ReactDOM.render(
<HelloWorld />,
document.getElementById('example')
);
</script>
</body>
</html>
// Hello Component 정의
var Hello = React.createClass({
render: function (){
return (
<span>Hello!!</span>
);
}
});
// World Component 정의
var World = React.createClass({
render: function (){
return (
<span>World!!</span>
);
}
});
// Hello와 World Component를 조합한 새로운 Component
var ThreeHelloOneWorld = React.createClass({
render: function (){
return (
<div>
<Hello />
<Hello />
<Hello />
<World />
</div>
)
}
});
ReactDOM.render(
<ThreeHelloOneWorld />,
document.getElementById('example')
);
// Timer Component 정의
var Timer = React.createClass({
// Component의 state를 정의하는 함수
getInitialState: function(){
return {
count: 0
};
},
// Component가 화면에 Mount 되면 실행되는 Life cycle 함수
componentDidMount: function(){
setInterval(this.tick, 1000);
},
// setState를 통해 상태를 갱신하는 함수
tick: function() {
this.setState({
count: this.state.count + 1
});
},
render: function () {
return (
<div>count {this.state.count}</div>
);
}
});
ReactDOM.render(
<Timer />,
document.getElementById('example')
);
var TeamCard = React.createClass({
render: function () {
return (
<div>
{this.props.teamName} 팀은 {this.props.floor}층에 있어요.
</div>
);
}
});
var TeamCards = React.createClass({
render: function () {
return (
<div>
<TeamCard teamName="발리" floor={5} />
<TeamCard teamName="티켓" floor={5} />
<TeamCard teamName="아발론" floor={6} />
</div>
)
}
});
// 마운팅하기
ReactDOM.render(
<TeamCards />,
document.getElementById('example')
);
// Timer Component 정의. props로 initialCount를 받아 자신의 state에 설정
var Timer = React.createClass({
getInitialState: function () {
return {
count: this.props.initialCount
};
},
componentDidMount: function (){
setInterval(this.tick, 1000);
},
tick: function() {
this.setState({
count: this.state.count + 1
});
},
render: function (){
return (
<div>
<TimerText count={this.state.count} />
</div>
)
}
});
// count를 props로 받아 화면에 뿌려주는 역할
var TimerText = React.createClass({
render: function (){
return (
<span>현재 Count는 {this.props.count} 이다!</span>
);
}
});
// Timer를 렌더링하면서 props로 initialCount를 넘겨줌
ReactDOM.render(
<Timer initialCount={105}/>,
document.getElementById('example')
);
var Timer = React.createClass({
propTypes: {
initialCount: React.PropTypes.number
},
......
});
var TimerText = React.createClass({
propTypes: {
count: React.PropTypes.number.isRequired
},
....
});
React.createClass({
propTypes: {
// You can declare that a prop is a specific JS primitive. By default, these
// are all optional.
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
optionalSymbol: React.PropTypes.symbol,
// Anything that can be rendered: numbers, strings, elements or an array
// (or fragment) containing these types.
optionalNode: React.PropTypes.node,
// A React element.
optionalElement: React.PropTypes.element,
// You can also declare that a prop is an instance of a class. This uses
// JS's instanceof operator.
optionalMessage: React.PropTypes.instanceOf(Message),
// You can ensure that your prop is limited to specific values by treating
// it as an enum.
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// An object that could be one of many types
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// An array of a certain type
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// An object with property values of a certain type
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// An object taking on a particular shape
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// You can chain any of the above with `isRequired` to make sure a warning
// is shown if the prop isn't provided.
requiredFunc: React.PropTypes.func.isRequired,
// A value of any data type
requiredAny: React.PropTypes.any.isRequired,
// You can also specify a custom validator. It should return an Error
// object if the validation fails. Don't `console.warn` or throw, as this
// won't work inside `oneOfType`.
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// You can also supply a custom validator to `arrayOf` and `objectOf`.
// It should return an Error object if the validation fails. The validator
// will be called for each key in the array or object. The first two
// arguments of the validator are the array or object itself, and the
// current item's key.
customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
},
/* ... */
});
propTypes에 선언된 타입과 props가 다른 경우..
isRequired로 선언된 props를 누락한 경우..
var ClickCounter = React.createClass({
getInitialState: function() {
return {
clickCounter: 0
};
},
render: function () {
return (
<div>
현재 버튼은 {this.state.clickCounter} 번 눌렸습니다.
<button onClick={this.handleClick}>눌러봅시다!</button>
</div>
)
},
handleClick: function(){
this.setState({
clickCounter: this.state.clickCounter + 1
});
}
});
ReactDOM.render(<ClickCounter/>, document.getElementById('example'));
var TodoApp = React.createClass({
getInitialState: function () {
return {
todos: [
{
todoText: '퇴근하기'
},
{
todoText: '퇴근이..진다..'
}
]
};
},
render: function () {
return (
<div>
<TodoText />
<TodoList todos={this.state.todos}/>
</div>
);
}
});
var TodoText = React.createClass({
render: function () {
return (
<form>
<input type="text"
placeholder="할 일을 입력하세요."/>
<button type="submit">추가하기</button>
</form>
);
}
});
var TodoList = React.createClass({
propTypes: {
todos: React.PropTypes.array
},
render: function () {
var todoLiComponents = [];
var todos = this.props.todos;
todos.forEach(function(todo, i){
todoLiComponents .push(
<li key={i}>
{todo.todoText}
</li>
);
});
return (
<ul>
{todoLiComponents }
</ul>
);
}
});
var TodoText = React.createClass({
getInitialState: function () {
return {
todoText: ''
};
},
render: function () {
return (
<form>
<input type="text"
placeholder="할 일을 입력하세요."
value={this.state.todoText}
onChange={this.handleTodoTextChange}/>
<button type="submit">추가하기</button>
</form>
)
},
handleTodoTextChange: function(e) {
this.setState({
todoText: e.target.value
});
}
});
var TodoText = React.createClass({
propTypes: {
onAddTodo: React.PropTypes.func.isRequired
},
getInitialState: function () {
return {
todoText: ''
};
},
render: function () {
return (
<form onSubmit={this.handleSubmit}>
<input type="text"
placeholder="할 일을 입력하세요."
value={this.state.todoText}
onChange={this.handleTodoTextChange}/>
<button type="submit">추가하기</button>
</form>
)
},
handleTodoTextChange: function(e) {
this.setState({
todoText: e.target.value
});
},
handleSubmit: function(e) {
e.preventDefault();
this.props.onAddTodo(this.state.todoText);
this.setState({
todoText: ''
});
}
});
var TodoApp = React.createClass({
getInitialState: function () {
return {
todos: [
{
todoText: '퇴근이..진다..'
},
{
todoText: '발리 여행가기'
}
]
};
},
render: function () {
return (
<div>
<TodoText onAddTodo={this.handleAddTodo}/>
<TodoList todos={this.state.todos} />
</div>
)
},
handleAddTodo: function(todoText) {
// 기존 state의 todos 를 clone
var newTodos = this.state.todos.slice();
newTodos.push({
todoText: todoText
});
this.setState({
todos: newTodos
});
}
});
var TodoApp = React.createClass({
getInitialState: function () {
return {
todos: []
};
},
componentDidMount: function() {
$.get('http://demo4539895.mockable.io/todo')
.done(function(result){
this.setState({
todos: result
});
}.bind(this));
},
render: function () {
return (
<div>
<TodoText onAddTodo={this.handleAddTodo}/>
<TodoList todos={this.state.todos} />
</div>
)
},
handleAddTodo: function(todoText) {
// 기존 state의 todos 를 clone
var newTodos = this.state.todos.slice();
newTodos.push({
todoText: todoText
});
this.setState({
todos: newTodos
});
}
});
var TodoApp = React.createClass({
...
render: function () {
return (
<div>
<TodoText onAddTodo={this.handleAddTodo}/>
{// onCompleted props 추가 }
<TodoList todos={this.state.todos}
onCompleted={this.handleCompleted}/>
</div>
)
},
handleCompleted: function(index) {
// props로 넘어간 이 핸들러가 호출되면서 파라메터로 완료처리할 todo의 index를 넘긴다
// clone 후 값을 바꾸고 setState
var newTodos = this.state.todos.slice();
newTodos[index].isCompleted = true;
// state가 바뀌면서 다시 화면이 그려짐
this.setState({
todos: newTodos
});
}
...
});
var TodoList = React.createClass({
propTypes: {
todos: React.PropTypes.arrayOf(React.PropTypes.shape({
todoText: React.PropTypes.string,
isCompleted: React.PropTypes.bool
})).isRequired,
onCompleted: React.PropTypes.func.isRequired
},
render: function () {
var todoLis = [];
var todos = this.props.todos;
todos.forEach(function(todo, i){
// todo의 isCompleted 값에 따라 동적으로 completed className을 설정
todoLis.push(
<li key={i}
className={todo.isCompleted ? 'completed' : ''}
onClick={this.handleClick}>
{todo.todoText}
</li>
)
}.bind(this));
return (
<ul>
{todoLis}
</ul>
)
},
// todo 클릭 시 이벤트 핸들러. 이벤트가 일어난 지점의 index를 구해서 props의 onCompleted Callback 호출
handleClick: function (e) {
this.props.onCompleted($(e.target).index());
}
});
var TodoStatus = React.createClass({
propTypes: {
todos: React.PropTypes.array.isRequired
},
render: function (){
var todos = this.props.todos;
var completedCount = todos.filter(function(todo){
return todo.isCompleted;
}).length;
return (
<div>
{todos.length} 개의 할 일 중에 {completedCount}개가 완료됨
</div>
)
}
});
Todo의 상태를 보여주는 TodoStatus Component 추가
...
render() {
return (
<div>
<TodoText onAddTodo={this.handleAddTodo}/>
<TodoList todos={this.state.todos} onCompleted={this.handleCompleted}/>
<TodoStatus todos={this.state.todos} />
</div>
)
}
...
TodoApp render 에 새 Component 추가
class HelloES6React extends React.Component {
render() {
const {name} = this.props;
return (
<div>hi!!!{name}</div>
)
}
}
HelloES6React.propTypes = {
name: React.PropTypes.string.isRequired
};
ReactDOM.render(<HelloES6React name="로토"/>, document.getElementById('example'));
class TodoApp extends React.Component {
constructor() {
super();
this.state = {
todos: []
};
}
componentDidMount () {
$.get('http://demo4539895.mockable.io/todo')
.done((result) => {
this.setState({
todos: result
});
});
}
render() {
return (
<div>
<TodoText onAddTodo={this.handleAddTodo.bind(this)}/>
<TodoList todos={this.state.todos} onCompleted={this.handleCompleted.bind(this)}/>
</div>
)
}
handleAddTodo (todoText) {
// 기존 state의 todos 를 clone
var newTodos = this.state.todos.slice();
newTodos.push({
todoText: todoText
});
this.setState({
todos: newTodos
});
}
}
class TodoApp extends React.Component {
// constructor 생략
state = {
todos: []
}
componentDidMount () {
$.get('http://demo4539895.mockable.io/todo')
.done((result) => {
this.setState({
todos: result
});
});
}
render() {
return (
<div>
<TodoText onAddTodo={this.handleAddTodo}/>
<TodoList todos={this.state.todos}
onCompleted={this.handleCompleted}/>
</div>
)
}
handleAddTodo = (todoText) => {
// 기존 state의 todos 를 clone
var newTodos = this.state.todos.slice();
newTodos.push({
todoText: todoText
});
this.setState({
todos: newTodos
});
};
class TodoList extends React.Component {
static propTypes = {
todos: React.PropTypes.array,
onCompleted: React.PropTypes.func.isRequired
};
render () {
var todoLis = [];
var todos = this.props.todos;
todos.forEach((todo, i) => {
todoLis.push(
<li key={i}
className={todo.isCompleted ? 'completed' : ''}
onClick={this.handleClick}>
{todo.todoText}
</li>
)
});
return (
<ul>
{todoLis}
</ul>
)
}
handleClick = (e) => {
this.props.onCompleted($(e.target).index());
};
}
By 김태희(로토/Roto) [Travel Systems CX]
쿠키 7기 강의자료