2woongjae@gmail.com
2woongjae@gmail.com
class HelloMessage extends React.Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}
ReactDOM.render(<HelloMessage name="Jane" />, mountNode);class HelloMessage extends React.Component {
  render() {
    return React.createElement(
      "div",
      null,
      "Hello ",
      this.props.name
    );
  }
}
ReactDOM.render(React.createElement(HelloMessage, { name: "Jane" }), mountNode);{
  "name": "start-project-babel",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "babel-core": "^6.25.0",
    "babel-loader": "^7.0.0",
    "babel-plugin-transform-react-jsx": "^6.24.1",
    "babel-preset-env": "^1.5.2",
    "react": "^15.6.1",
    "react-dom": "^15.6.1",
    "webpack": "^2.6.1",
    "webpack-dev-server": "^2.4.5"
  }
}{
  "name": "start-project-nocra",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "@types/react": "^15.0.29",
    "@types/react-dom": "^15.5.0",
    "react": "^15.6.1",
    "react-dom": "^15.6.1",
    "ts-loader": "^2.1.0",
    "typescript": "^2.3.4",
    "webpack": "^2.6.1",
    "webpack-dev-server": "^2.4.5"
  }
}
const path = require('path');
module.exports = {
    // input 설정
    entry: './src/index.tsx',
    // output 설정
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    // 
    resolve: {
        extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
    },
    // transformations 설정
    module: {
        rules: [
            {
                test: /\.(ts|tsx)$/,
                loader: "ts-loader",
            },
            {
                enforce: "pre",
                test: /\.(ts|tsx)$/,
                loader: "tslint-loader"
            },
            {
                enforce: "pre",
                test: /\.js$/,
                loader: "source-map-loader"
            }
        ]
    },
    // sourcemaps 설정 : Enable sourcemaps for debugging webpack's output.
    devtool: 'source-map',
    // server 설정
    devServer: {
        contentBase: path.join(__dirname, 'src'),
        compress: true,
        historyApiFallback: true
    }
};{
    "compilerOptions": {
        "outDir": "dist",
        "sourceMap": true,
        "noImplicitAny": true,
        "module": "commonjs",
        "moduleResolution": "node",
        "target": "es5",
        "lib": ["es6", "dom"],
        "jsx": "react"
    },
    "include": [
        "./src/**/*"
    ]
}Marks:start-project-nocra mark$ yarn add tslint, tslint-loader, tslint-react -D{
    "extends": ["tslint-react"],
    "rules": {
        "no-console": [
            true,
            "log",
            "error",
            "debug",
            "info",
            "time",
            "timeEnd",
            "trace"
        ]
    }
}
{
  "name": "start-project-cra",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@types/jest": "^20.0.1",
    "@types/node": "^8.0.1",
    "@types/react": "^15.0.30",
    "@types/react-dom": "^15.5.0",
    "react": "^15.6.1",
    "react-dom": "^15.6.1"
  },
  "devDependencies": {
    "react-scripts-ts": "2.2.0"
  },
  "scripts": {
    "start": "react-scripts-ts start",
    "build": "react-scripts-ts build",
    "test": "react-scripts-ts test --env=jsdom",
    "eject": "react-scripts-ts eject"
  }
}{
  "compilerOptions": {
    "outDir": "build/dist", // 빌드 결과물 폴더
    "module": "commonjs", // 빌드 결과의 모듈 방식은 commonjs
    "target": "es5", // 빌드 결과물은 es5 방식으로
    "lib": ["es6", "dom"], // 라이브러리는 es6 와 dom
    "sourceMap": true, // .map.js 파일도 함께 생성
    "allowJs": true, // JS 파일도 컴파일 대상
    "jsx": "react", // jsx 구문 사용 가능
    "moduleResolution": "node", // 모듈 해석 방식은 node 처럼
    "rootDir": "src", // 컴파일할 대상들이 들어있는 폴더 (루트 폴더)
    "forceConsistentCasingInFileNames": true, // https://github.com/TypeStrong/ts-loader/issues/89
    "noImplicitReturns": true, // 제대로 리턴 다 안되면 에러
    "noImplicitThis": true, // this 표현식에 암시적으로 any 로 추론되면
    "noImplicitAny": true, // 암시적으로 선언되었는데 any 로 추론되면
    "strictNullChecks": true, // null 이나 undefined 을 서브 타입으로 사용하지 못하게 함
    "suppressImplicitAnyIndexErrors": true, // 인덱싱 시그니처가 없는 경우, 인덱스를 사용했을때 noImplicitAny 에 의해 에러가 뜨는 것을 방지
    "noUnusedLocals": true // 사용 안하는 로컬 변수가 있으면 에러
  },
  "exclude": [
    "node_modules",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ],
  "types": [
    "typePatches" // 자동으로 패치되기 때문에 이렇게 막아놓은 듯
  ]
}{
    "extends": ["tslint-react"],
    "rules": {
        "align": [
            true,
            "parameters",
            "arguments",
            "statements"
        ],
        "ban": false,
        "class-name": true,
        "comment-format": [
            true,
            "check-space"
        ],
        "curly": true,
        "eofline": false,
        "forin": true,
        "indent": [ true, "spaces" ],
        "interface-name": [true, "never-prefix"],
        "jsdoc-format": true,
        "jsx-no-lambda": false,
        "jsx-no-multiline-js": false,
        "label-position": true,
        "max-line-length": [ true, 120 ],
        "member-ordering": [
            true,
            "public-before-private",
            "static-before-instance",
            "variables-before-functions"
        ],
        "no-any": true,
        "no-arg": true,
        "no-bitwise": true,
        "no-console": [
            true,
            "log",
            "error",
            "debug",
            "info",
            "time",
            "timeEnd",
            "trace"
        ],
        "no-consecutive-blank-lines": true,
        "no-construct": true,
        "no-debugger": true,
        "no-duplicate-variable": true,
        "no-empty": true,
        "no-eval": true,
        "no-shadowed-variable": true,
        "no-string-literal": true,
        "no-switch-case-fall-through": true,
        "no-trailing-whitespace": false,
        "no-unused-expression": true,
        "no-use-before-declare": true,
        "one-line": [
            true,
            "check-catch",
            "check-else",
            "check-open-brace",
            "check-whitespace"
        ],
        "quotemark": [true, "single", "jsx-double"],
        "radix": true,
        "semicolon": [true, "always"],
        "switch-default": true,
        "trailing-comma": false,
        "triple-equals": [ true, "allow-null-check" ],
        "typedef": [
            true,
            "parameter",
            "property-declaration"
        ],
        "typedef-whitespace": [
            true,
            {
                "call-signature": "nospace",
                "index-signature": "nospace",
                "parameter": "nospace",
                "property-declaration": "nospace",
                "variable-declaration": "nospace"
            }
        ],
        "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
        "whitespace": [
            true,
            "check-branch",
            "check-decl",
            "check-module",
            "check-operator",
            "check-separator",
            "check-type",
            "check-typecast"
        ]
    }
}import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
// react-dom 라이브러리를 이용해서 DOM 에 리액트 컴포넌트를 매칭
ReactDOM.render(
  <App />,
  document.getElementById('root') as HTMLElement
);
registerServiceWorker();import * as React from 'react';
import './App.css';
const logo = require('./logo.svg');
class App extends React.Component<{}, null> {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.tsx</code> and save to reload.
        </p>
      </div>
    );
  }
}
export default App;class App extends React.Component<{}, null> {
  render() {
    return (
      <h2>Hello World</h2>
    );
  }
}
class Component<P, S> {
  constructor(props?: P, context?: any);
  setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback?: () => any): void;
  setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
  forceUpdate(callBack?: () => any): void;
  render(): JSX.Element | null | false;
  // React.Props<T> is now deprecated, which means that the `children`
  // property is not available on `P` by default, even though you can
  // always pass children as variadic arguments to `createElement`.
  // In the future, if we can define its call signature conditionally
  // on the existence of `children` in `P`, then we should remove this.
  props: Readonly<{ children?: ReactNode }> & Readonly<P>;
  state: Readonly<S>;
  context: any;
  refs: {
    [key: string]: ReactInstance
  };
}class App extends React.Component<{ name: string }, null> {
  render() {
    return (
      <h2>Hello {this.props.name}</h2>
    );
  }
}
ReactDOM.render(
  <App name="Mark" />,
  document.getElementById('root') as HTMLElement
);
class Component<P, S> {
  constructor(props?: P, context?: any);
  setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback?: () => any): void;
  setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
  forceUpdate(callBack?: () => any): void;
  render(): JSX.Element | null | false;
  // React.Props<T> is now deprecated, which means that the `children`
  // property is not available on `P` by default, even though you can
  // always pass children as variadic arguments to `createElement`.
  // In the future, if we can define its call signature conditionally
  // on the existence of `children` in `P`, then we should remove this.
  props: Readonly<{ children?: ReactNode }> & Readonly<P>;
  state: Readonly<S>;
  context: any;
  refs: {
    [key: string]: ReactInstance
  };
}class App extends React.Component<{ name: string; }, { age: number; }> {
  render() {
    return (
      // <h2>{this.props.name}</h2>
      <h2>{this.props.name} - {this.state.age}</h2>
    );
  }
}class App extends React.Component<{ name: string; }, { age: number; }> {
  public state = {
    age: 35
  };
  render() {
    return (
      <h2>Hello {this.props.name} - {this.state.age}</h2>
    );
  }
}
class App extends React.Component<{ name: string; }, { age: number; }> {
  constructor(props: { name: string; }) {
    super(props);
    this.state = {
      age: 35
    };
  }
  render() {
    return (
      <h2>Hello {this.props.name} - {this.state.age}</h2>
    );
  }
}
class Component<P, S> {
  constructor(props?: P, context?: any);
  setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback?: () => any): void;
  setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
  forceUpdate(callBack?: () => any): void;
  render(): JSX.Element | null | false;
  // React.Props<T> is now deprecated, which means that the `children`
  // property is not available on `P` by default, even though you can
  // always pass children as variadic arguments to `createElement`.
  // In the future, if we can define its call signature conditionally
  // on the existence of `children` in `P`, then we should remove this.
  props: Readonly<{ children?: ReactNode }> & Readonly<P>;
  state: Readonly<S>;
  context: any;
  refs: {
    [key: string]: ReactInstance
  };
}class App extends React.Component<{ name: string; }, { age: number; }> {
  constructor(props: { name: string; }) {
    super(props);
    this.state = {
      age: 35
    };
    setInterval(() => {
      // this.state.age = this.state.age + 1;
      this.setState({
        age: this.state.age + 1
      });
    }, 1000);
  }
  render() {
    return (
      <h2>Hello {this.props.name} - {this.state.age}</h2>
    );
  }
}
class Component<P, S> {
  constructor(props?: P, context?: any);
  setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback?: () => any): void;
  setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
  forceUpdate(callBack?: () => any): void;
  render(): JSX.Element | null | false;
  // React.Props<T> is now deprecated, which means that the `children`
  // property is not available on `P` by default, even though you can
  // always pass children as variadic arguments to `createElement`.
  // In the future, if we can define its call signature conditionally
  // on the existence of `children` in `P`, then we should remove this.
  props: Readonly<{ children?: ReactNode }> & Readonly<P>;
  state: Readonly<S>;
  context: any;
  refs: {
    [key: string]: ReactInstance
  };
}export interface AppProps {
  name: string;
}
interface AppState {
  age: number;
}
class App extends React.Component<AppProps, AppState> {
  constructor(props: AppProps) {
    super(props);
    this.state = {
      age: 35
    };
    setInterval(() => {
      this.setState({
        age: this.state.age + 1
      });
    }, 1000);
  }
  render() {
    return (
      <h2>Hello {this.props.name} - {this.state.age}</h2>
    );
  }
}const StatelessComponent = (props: AppProps) => {
  return (
    <h2>{props.name}</h2>
  );
}
const StatelessComponent: React.SFC<AppProps> = (props) => {
  return (
    <h2>{props.name}</h2>
  );
}
type SFC<P> = StatelessComponent<P>;
interface StatelessComponent<P> {
  (props: P & { children?: ReactNode }, context?: any): ReactElement<any>;
  propTypes?: ValidationMap<P>;
  contextTypes?: ValidationMap<any>;
  defaultProps?: Partial<P>;
  displayName?: string;
}render() {
  return (
    <div>
      <h2>Hello {this.props.name} - {this.state.age}</h2>
      <StatelessComponent name="Anna" />
      <StatelessComponent name="Anna">여기는 칠드런입니다. 있을수도 있고 없을 수도 있고</StatelessComponent>
    </div>
  );
}
const StatelessComponent: React.SFC<AppProps> = (props) => {
  return (
    <h2>{props.name}, {props.children}</h2>
  );
}
type SFC<P> = StatelessComponent<P>;
interface StatelessComponent<P> {
  (props: P & { children?: ReactNode }, context?: any): ReactElement<any>;
  propTypes?: ValidationMap<P>;
  contextTypes?: ValidationMap<any>;
  defaultProps?: Partial<P>;
  displayName?: string;
}import * as React from 'react';
import './App.css';
export interface AppProps {
  name: string;
}
export interface AppState {
  age: number;
}
class App extends React.Component<AppProps, AppState> {
  private _interval: number;
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
    this.state = {
      age: 35
    };
  }
  componentWillMount() {
    console.log('App componentWillMount');
  }
  componentDidMount() {
    console.log('App componentDidMount');
    this._interval = window.setInterval(
      () => {
        this.setState({
          age: this.state.age + 1
        });
      },
      1000
    );
  }
  componentWillUnmount() {
    console.log('App componentWillUnmount');
    clearInterval(this._interval);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <h2>Hello {this.props.name} - {this.state.age}</h2>
      </div>
    );
  }
}
export default App;
import * as React from 'react';
import './App.css';
export interface AppProps {
  name: string;
}
export interface AppState {
  age: number;
}
class App extends React.Component<AppProps, AppState> {
  private _interval: number;
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
    this.state = {
      age: 35
    };
  }
  componentWillMount() {
    console.log('App componentWillMount');
  }
  componentDidMount() {
    console.log('App componentDidMount');
    this._interval = window.setInterval(
      () => {
        this.setState({
          age: this.state.age + 1
        });
      },
      1000
    );
  }
  componentWillUnmount() {
    console.log('App componentWillUnmount');
    clearInterval(this._interval);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <h2>Hello {this.props.name} - {this.state.age}</h2>
      </div>
    );
  }
}
export default App;
constructor
componentWillMount
render
componentDidMount
  componentWillReceiveProps(nextProps: AppProps) {
    console.log(`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`);
  }
  shouldComponentUpdate(nextProps: AppProps, nextState: AppState): boolean {
    console.log(`App shouldComponentUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
    return true;
  }
  componentWillUpdate(nextProps: AppProps, nextState: AppState) {
    console.log(`App componentWillUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
  }
  componentDidUpdate(prevProps: AppProps, prevState: AppState) {
    console.log(`App componentDidUpdate : ${JSON.stringify(prevProps)}, ${JSON.stringify(prevState)}`);
  }componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
    this.state = {
      age: 35
    };
    this._reset = this._reset.bind(this);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <h2>Hello {this.props.name} - {this.state.age}</h2>
        <button onClick={this._reset}>리셋</button>
      </div>
    );
  }
  private _reset(): void {
    this.setState({
      age: 35
    });
  }import * as React from 'react';
import './App.css';
export interface AppProps {
  name: string;
}
export interface AppState {
  age: number;
  company: string;
}
class App extends React.Component<AppProps, AppState> {
  private _interval: number;
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
    this.state = {
      age: 35,
      company: 'Studio XID'
    };
    this._reset = this._reset.bind(this);
    this._change = this._change.bind(this);
  }
  componentWillMount() {
    console.log('App componentWillMount');
  }
  componentDidMount() {
    console.log('App componentDidMount');
    this._interval = window.setInterval(
      () => {
        this.setState({
          age: this.state.age + 1
        });
      },
      1000
    );
  }
  componentWillUnmount() {
    console.log('App componentWillUnmount');
    clearInterval(this._interval);
  }
  componentWillReceiveProps(nextProps: AppProps) {
    console.log(`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`);
  }
  shouldComponentUpdate(nextProps: AppProps, nextState: AppState): boolean {
    console.log(`App shouldComponentUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
    return true;
  }
  componentWillUpdate(nextProps: AppProps, nextState: AppState) {
    console.log(`App componentWillUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
  }
  componentDidUpdate(prevProps: AppProps, prevState: AppState) {
    console.log(`App componentDidUpdate : ${JSON.stringify(prevProps)}, ${JSON.stringify(prevState)}`);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <h2>Hello {this.props.name} - {this.state.age}</h2>
        <button onClick={this._reset}>리셋</button>
        <input type="text" onChange={this._change} value={this.state.company} />
      </div>
    );
  }
  private _reset(): void {
    this.setState({
      age: 35
    });
  }
  private _change(e: React.ChangeEvent<HTMLInputElement>): void {
    this.setState({
      company: e.target.value
    });
  }
}
export default App;
// 사용시에 name props 를 쓰지 않으면,
ReactDOM.render(
  <App />,
  document.getElementById('root') as HTMLElement
);
// 이렇게 name 을 물음표를 이용해 옵셔널하게 처리
export interface AppProps {
  name?: string;
}
// 클래스 안에 static 메서드를 이용해서 디폴트 값을 작성한다.
public static defaultProps = {
  name: 'Default'
};
// type definition 에 따르면 Props 의 부분집합이다.
defaultProps?: Partial<P>;export interface AppProps {
  name?: string;
}
const StatelessComponent: React.SFC<AppProps> = (props) => {
  return (
    <h2>{props.name}</h2>
  );
}
StatelessComponent.defaultProps = {
  name: 'Default'
};export interface AppProps {
  name?: string;
}
const StatelessComponent: React.SFC<AppProps> = ({name = 'Default'}) => {
  return (
    <h2>{props.name}</h2>
  );
}import * as React from 'react';
import './App.css';
export interface AppProps {
}
export interface AppState {
  toGrandChild: string;
}
class App extends React.Component<AppProps, AppState> {
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
    this.state = {
      toGrandChild: '아직 안바뀜'
    };
    this._clickToGrandChild = this._clickToGrandChild.bind(this);
  }
  componentWillMount() {
    console.log('App componentWillMount');
  }
  componentDidMount() {
    console.log('App componentDidMount');
  }
  componentWillUnmount() {
    console.log('App componentWillUnmount');
  }
  componentWillReceiveProps(nextProps: AppProps) {
    console.log(`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`);
  }
  shouldComponentUpdate(nextProps: AppProps, nextState: AppState): boolean {
    console.log(`App shouldComponentUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
    return true;
  }
  componentWillUpdate(nextProps: AppProps, nextState: AppState) {
    console.log(`App componentWillUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
  }
  componentDidUpdate(prevProps: AppProps, prevState: AppState) {
    console.log(`App componentDidUpdate : ${JSON.stringify(prevProps)}, ${JSON.stringify(prevState)}`);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <Parent {...this.state} />
        <button onClick={this._clickToGrandChild}>GrandChild 의 값을 바꾸기</button>
      </div>
    );
  }
  private _clickToGrandChild(): void {
    this.setState({
      toGrandChild: '그랜드 차일드의 값을 변경'
    });
  }
}
interface ParentProp {
  toGrandChild: string;
}
const Parent: React.SFC<ParentProp> = (props) => {
  return (
    <div>
      <p>여긴 Parent</p>
      <Me {...props} />
    </div>
  );
};
interface MeProp {
  toGrandChild: string;
}
const Me: React.SFC<MeProp> = (props) => {
  return (
    <div>
      <p>여긴 Me</p>
      <Child {...props} />
    </div>
  );
};
interface ChildProp {
  toGrandChild: string;
}
const Child: React.SFC<ChildProp> = (props) => {
  return (
    <div>
      <p>여긴 Child</p>
      <GrandChild {...props} />
    </div>
  );
};
interface GrandChildProp {
  toGrandChild: string;
}
const GrandChild: React.SFC<GrandChildProp> = (props) => {
  return (
    <div>
      <p>여긴 GrandChild</p>
      <h3>{props.toGrandChild}</h3>
    </div>
  );
};
export default App;import * as React from 'react';
import './App.css';
export interface AppProps {
}
export interface AppState {
  fromGrandChild: string;
}
class App extends React.Component<AppProps, AppState> {
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
    this.state = {
      fromGrandChild: '아직 안바뀜'
    };
    this._clickFromGrandChild = this._clickFromGrandChild.bind(this);
  }
  componentWillMount() {
    console.log('App componentWillMount');
  }
  componentDidMount() {
    console.log('App componentDidMount');
  }
  componentWillUnmount() {
    console.log('App componentWillUnmount');
  }
  componentWillReceiveProps(nextProps: AppProps) {
    console.log(`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`);
  }
  shouldComponentUpdate(nextProps: AppProps, nextState: AppState): boolean {
    console.log(`App shouldComponentUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
    return true;
  }
  componentWillUpdate(nextProps: AppProps, nextState: AppState) {
    console.log(`App componentWillUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
  }
  componentDidUpdate(prevProps: AppProps, prevState: AppState) {
    console.log(`App componentDidUpdate : ${JSON.stringify(prevProps)}, ${JSON.stringify(prevState)}`);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <Parent clickFromGrandChild={this._clickFromGrandChild} />
        <p>{this.state.fromGrandChild}</p>
      </div>
    );
  }
  private _clickFromGrandChild(): void {
    this.setState({
      fromGrandChild: '그랜드 차일드로 부터 값이 변경되었음.'
    });
  }
}
interface ParentProp {
  clickFromGrandChild(): void;
}
const Parent: React.SFC<ParentProp> = (props) => {
  return (
    <div>
      <p>여긴 Parent</p>
      <Me {...props} />
    </div>
  );
};
interface MeProp {
  clickFromGrandChild(): void;
}
const Me: React.SFC<MeProp> = (props) => {
  return (
    <div>
      <p>여긴 Me</p>
      <Child {...props} />
    </div>
  );
};
interface ChildProp {
  clickFromGrandChild(): void;
}
const Child: React.SFC<ChildProp> = (props) => {
  return (
    <div>
      <p>여긴 Child</p>
      <GrandChild {...props} />
    </div>
  );
};
interface GrandChildProp {
  clickFromGrandChild(): void;
}
const GrandChild: React.SFC<GrandChildProp> = (props) => {
  return (
    <div>
      <p>여긴 GrandChild</p>
      <button onClick={props.clickFromGrandChild}>GrandChild 버튼</button>
    </div>
  );
};
export default App;function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}
function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}interface CustomTextInputProps {
  inputRef(element: HTMLInputElement): void;
}
function CustomTextInput(props: CustomTextInputProps) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}
function Parent(props: ParentProps) {
  return (
    <div>
      My input: <CustomTextInput inputRef={props.inputRef} />
    </div>
  );
}
interface ParentProps {
  inputRef(element: HTMLInputElement): void;
}
class App extends React.Component<AppProps, AppState> {
  inputElement: HTMLInputElement | null;
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
  }
  componentWillMount() {
    console.log('App componentWillMount');
  }
  componentDidMount() {
    console.log('App componentDidMount');
  }
  componentWillUnmount() {
    console.log('App componentWillUnmount');
  }
  componentWillReceiveProps(nextProps: AppProps) {
    console.log(`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`);
  }
  shouldComponentUpdate(nextProps: AppProps, nextState: AppState): boolean {
    console.log(`App shouldComponentUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
    return true;
  }
  componentWillUpdate(nextProps: AppProps, nextState: AppState) {
    console.log(`App componentWillUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
  }
  componentDidUpdate(prevProps: AppProps, prevState: AppState) {
    console.log(`App componentDidUpdate : ${JSON.stringify(prevProps)}, ${JSON.stringify(prevState)}`);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <Parent inputRef={element => this.inputElement = element} />
      </div>
    );
  }
}
export default App;  componentWillReceiveProps(nextProps: AppProps) {
    console.log(`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`);
  }
  shouldComponentUpdate(nextProps: AppProps, nextState: AppState): boolean {
    console.log(`App shouldComponentUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
    return true;
  }
  componentWillUpdate(nextProps: AppProps, nextState: AppState) {
    console.log(`App componentWillUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
  }
  componentDidUpdate(prevProps: AppProps, prevState: AppState) {
    console.log(`App componentDidUpdate : ${JSON.stringify(prevProps)}, ${JSON.stringify(prevState)}`);
  }componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
import * as React from 'react';
import './App.css';
export interface AppProps {
}
export interface AppState {
  todo: string[];
}
class App extends React.PureComponent<AppProps, AppState> {
  constructor(props: AppProps) {
    console.log('App constructor');
    super(props);
    this.state = {
      todo: ['First']
    };
    this._addSecond = this._addSecond.bind(this);
  }
  componentWillMount() {
    console.log('App componentWillMount');
  }
  componentDidMount() {
    console.log('App componentDidMount');
  }
  componentWillUnmount() {
    console.log('App componentWillUnmount');
  }
  componentWillReceiveProps(nextProps: AppProps) {
    console.log(`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`);
  }
  /*
  shouldComponentUpdate(nextProps: AppProps, nextState: AppState): boolean {
    console.log(`App shouldComponentUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
    return true;
  }
  */
  componentWillUpdate(nextProps: AppProps, nextState: AppState) {
    console.log(`App componentWillUpdate : ${JSON.stringify(nextProps)}, ${JSON.stringify(nextState)}`);
  }
  componentDidUpdate(prevProps: AppProps, prevState: AppState) {
    console.log(`App componentDidUpdate : ${JSON.stringify(prevProps)}, ${JSON.stringify(prevState)}`);
  }
  render() {
    console.log('App render');
    return (
      <div>
        <p>{this.state.todo.join(', ')}</p>
        <button onClick={this._addSecond}>Second 추가</button>
      </div>
    );
  }
  private _addSecond(): void {
    const todo: string[] = this.state.todo;
    todo.push('Second');
    this.setState({
      todo: todo
    });
  }
}
export default App;