코규리
article thumbnail

React에 대한 개인학습을 기록합니다.

 

체크리스트

TITLE
연습용 레포 생성
리액트 개요 확인
JSX다루기
리액트 컴포넌트
리액트 스타일링
컴포넌트 복잡하게 다루기
속성 전달
JSX 복잡하게 다루기
상태 다루기, 카운터 설정
데이터를 UI로 불러오기
이벤트 다루기
컴포넌트 생명주기 (생명주기 메소드)
DOM 엘리먼트 접근하기
  라우터를 통한 싱글 페이지 앱 구축
  Todo list 앱 제작
  리액트 개발 환경 구성

 


10. 이벤트

: 모든 제스처나 상호작용 촉발을 가능하게 하는 접합체


 10.1. 예제시작하기

10.1.1 시작 파일

<!DOCYPE html>
<html>

<head>
    <title>ch10</title>
    <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
    <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
    <script src="https://cdnjs.Cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>

    <style>
        #container {
            padding: 50px;
            background-color: #FFF;
        }
    </style>
</head>

<body>
    <div id="container"></div>
    <script type="text/babel">
        
        var destination = document.querySelector("#container");
        var Counter = React.createClass({
            render: function() {
                var textStyle = {
                    fontSize: 72,
                    fontFamily: "sans-serif",
                    color : "#333",
                    fontWeight: "bold"
                };
                return(
                    <div style={textStyle}>     
                        {this.props.display}
                    </div>
                ); 
            }
        });
        
        var CounterParent = React.createClass({
            getInitialState: function() {
                return{
                    count: 0
                };
            },
            render: function() {
                var backgroundStyle = {
                    padding: 50,
                    backgroundColor: "#FFC53A",
                    width: 250,
                    height: 100,
                    borderRadius: 10,
                    textAlign: "center"
                };

                var buttonStyle = {
                    fontSize: "lem",
                    width: 30,
                    height: 30,
                    fontFamily: "sans-serif",
                    color: "#333",
                    fontWeight: "bold",
                    lineHeight: "3px"
                };

                return (
                    <div style={backgroundStyle}>
                        <Counter display={this.state.count}/>
                        <button style={buttonStyle}>+</button>
                    </div>
                );
            }
        });

        ReactDOM.render(
            <div>
                <CounterParent/>
            </div>,
            destination
        );
    </script>
</body>
</html>

 10.2. 이벤트 속성

  • 이벤트는 이벤트 핸들러의 인자로 전달됨
  • 이벤트 인자에는 종류에 따른 한 뭉탱이 속성들이 존재
  • 일반적 DOM 세계에서 각 이벤트는 자신만의 타입 존재

10.2.1. 합성이벤트

  • JSX에 이벤트 지정할 경우, 실은 DOM이벤트를 직접 다루지 않음
  • 합성 이벤트라는 리액트에 특정적 이벤트 유형인 SyntheticEvent를 다룬다
  • 이벤트 핸들러는 native이벤트 타입을 받지 않는다
    • 네이티브 이벤트 EX): MouseEvent, KeyboardEvent
    • 그러나 SyntheticEvent는 다음 속성들을 포함한다

    

속성 타입
bubbles boolean
cancelable boolean
currentTarget DOMEventTarget
defaultPrevented boolean
eventPhase number
isTrusted boolean
nativeEvent DOMEvent
preventDefault() void
isDefaultPrevented() boolean
isPropagationStopped boid
target DOMEventTarget
timeStamp number
type string

 ... (나머지 자료 생략)

    

 

  • 결국, SyntheticEvent를 가지고 평범한 DOM에서 했던 것과 동일한 작업 수행 가능
  • SyntheticEvent는 DOM이벤트를 래핑하는 것이며, 일대일 대응은 아니다
    • 일부 DOM이벤트는 리액트에 존재하지 않는다
    • 이슈가 있을 시, 리액트의 SyntheticEvent문서를 ㅊ마고

10.3. 이벤트 속성다루기

10.3.1. shiftKey 속성

  • 마우스 이벤트를 래핑하는 SyntheticEvent에 포함되어 있음
  • 마우스 이벤트 발생시 시프트 키가 눌려있다면 shifKey속성값:True, 그게 아니면 False

10.3.2. increase함수 코드 수정

increase: function(e) {
                var currentCount = this.state.count;

                    if(e.shiftKey) {
                        currentCount +=10;
                    } else {
                        currentCount += 1;
                    }
                
                    this.setState({
                        count: currentCount
                    });
            },

 

10.4. 복잡한, 일반적인 상황에서의 이벤트 처리 흐름

10.4.1. 컴포넌트의 이벤트는 직접 리스닝 없다

  • 가정: 사용자가 사용할 수 있는 어떠한 UI엘리먼트 하나를 가진 컴포넌트가 존재한다
    • 다음 코드에서처럼 이벤트를 처리할 수 있을 것이다
var CounterParent = React.createClass({
	getInitialState: function(){
		return {
			count:0
		};
	},
		increase: function() {
		this.setState({
			count: this.state.count +1
		});
	},
	render: function() {
		return{
			<div>
				<Counter display={this.state.count}/>
				<PlusButton onClick={this.increase}/>  //이부분 주의!
			</div>
		};
	}
});

//PlusButton
var PlusButton = React.createClass({ 
	render: function(){
		return (
			<button>
				+
			</button>
		);
	}
});
  •     표면적으로는 JSX코드가 유효하다 (PlusButton 컴포넌트 클릭시 increase함수 호출됨)
  •     PlusButton 컴포넌트는 아무 것도 하지 않는다 ( HTMl엘리먼트 하나만 리턴한다, 이벤트 리스닝 불가)
    •         컴포넌트는 DOM엘리먼트는 감싸는 rapper이기 때문임 (button에 이벤트 접근이 안 됨.)
  • 이벤트 리스닝과 속성 선언의 구분이 필요하다
    •     명확한 답은 없지만, 이벤트 핸들러를 속성처럼 다루고 컴포넌트에 전달할 수 있는 선택지가 있다
    •     컴포넌트 안에서 DOM엘리먼트에 이벤트를 할당하고 속성 값으로 이벤트 핸들러를 설정하면 된다
...
<PlusButton clickHandler = {this.increase}/> //이벤트 할당시킴
...

//PlusButton
var PlusButton = React.createClass({ 
	render: function(){
		return (
			<button onClick = {this.props.clickHandler}>. //이벤트 추가, 핸들러 지정
				+
			</button>
		);
	}
});

    

10.4.2. 일반적인 DOM 이벤트 리스닝

  • 모든 이벤트가 SyntheticEvent에 대응하는 것은 아니다
    • 리스닝하고자 하는 이벤트에 접두사를 붙이고 대문자를 사용해 JSX안에 인라인 방식으로 지정했다 가정
    • 아래의 코드는 동작 X, 리액트가 인식할 수 있는 공식 이벤트가 아님
var Something = React.createClass({
	handleMyEvent: function(e){
		//이벤트 처리
	},
	render: function() {
		return(
			<div onMyWeiredEvent = {this.handleMyEvent}>Hello!</div>
		);
	}
});
  •     컴포넌트 생명주기 메소드에서 addEventListener를 사용하는 전통 방법쓰기
var Something = React.createClass({
	handleMyEvent: function(e) {
		//이벤트처리
	},
		componentDidMount: funtion(){
			window.addEventListener("someEvent", this.handleMyEvent);
	},
		componentWillUnmount: function(){
			window.remoevEventListener("someEent", this.handleMyEvent);
	},
		render: function() {
			return(
				div>Hello!</div>
		);
	}
});
  •     컴포넌트 렌더링시 자동 호출되는 componentDidMount메소드에서 someEvent리스닝 시작
    • 이때, ddEventListener에 이벤트와 이벤트 핸들러를 전달하는 방법을 사용
  •     컴포넌트가 소멸될 때 이 이벤트 리스너도 제거해야 한다(componentWilUnmount)

10.4.3. 이벤트 핸들러에서 this 의미

  • 리액트 이벤트 핸들러 안의 this ≠ DOM안에서의 this
    • 리액트가 아닌 곳에서 이벤트 핸들러 안의 this는 리스닝 하는 대상 엘리먼트를 참조함
var CounterParent = React.createClass({
	getInitialState: function() {
		return {
			count: 0
		};
	},
	increase: function(e) {
		console.log(this); // 얘는 엘리먼트가 아니라 컴포넌트(Counterparent)를 나타내지.
		this.setState({
			count: this.state.count +1
		});
	},
	render: function() {
		return (
			<div>
				<Counter display={this.staet.count}/>
				<button onClick={this.increase} + </button>
			</div>
		);
	}
});
  •     리액트에서는 이벤트 핸들러 안의 this는 이벤트 핸들러가 속한 컴포넌트를 참조함
var CounterParent = React.createClass({
	getInitialState: function() {
		return {
			count: 0
		};
	},
	increase: function(e) {
		console.log(this); // 얘는 엘리먼트가 아니라 컴포넌트(Counterparent)를 나타내지.
		this.setState({
			count: this.state.count +1
		});
	},
	render: function() {
		return (
			<div>
				<Counter display={this.staet.count}/>
				<button onClick={this.increase} + </button>
			</div>
		);
	}
});

10.4.4. 그렇게 하는가

  • 브라우저 호환성
    • 모든 네이티브 이벤트를 SyntheticEvent타입 객체로 래핑 ⇒ 호환되지 않는 환경에서도 이벤트 처리를 동일한 방법으로 할 수 있게 함
  • 성능 향상
    • 이벤트 핸들러가 많아질수록 더 많은 메모리를 차지하는데, 잉 대한 수동 조치는 지루함 때론 불가능.
    • 리액트가 이 부분을 영리하게 대처함
  • 리액트는 이벤트 핸들러를 DOM엘리먼트에 절대 직접 부착하지 않음
    • 문서의 최상위에 있는 하나의 이벤트 핸들러를 사용함
      • 이 이벤트 핸들러가 모든 이벤트를 리스닝함, 이벤트 발생 시 적합한 개별 핸들러를 호출하는 책임을 짐

 10.5. 정리

  • 앱 개발의 이벤트 처리를 시간이 많이 든다
  • 이벤트 리스닝과 지정에 대한 기본적 방법, 일반적이 않은 상황의 이벤트 처리 방법을 앎

 

 


11. 컴포넌트 생명주기

생명주기 메소드: 컴포넌트가 어떤 작업 단계에 진입시 자동 호출되는 메소드


  • 컴포넌트 일생의 중요한 시점을 알 수 있음
  • 원하는 시점에 특정 작업을 수행하도록 할 수 있음

 11.1. 생명주기 메소드 작동 확인

https://www.kirupa.com/react/lifecycle_example.htm

 11.2. 초기 렌더링 단계

: getDefaultProps → getInitialState → componentWillMount → render → componentDIdMount

11.2.1. getDefaultProps

	getDefaultProps: fuction(){
		cnosole.log("getDefaultProps:default prop time!");
		return {};
	},
  • this.props의 기본값을 지정할 수 있게 함
  • 컴포넌트가 만들어지려 할 때 || 부모로부터 속성이 전달됐을 떄 호출된다

11.2.2. getInitialState

	getIntitialState: function() {
		console.log("getInitialState: Default state time!");
		return {
			count: 0
		};
	},
  • this.state의 기본값을 지정할 수 있게 함
  • getDefultProps와 마찬가지로 컴포넌트가 만들어지기 전에 호출됨

11.2.3. componentWillMount

	componentWillUpdate: function(newProps, newState) {
		console.log("componentWillUpdate: Component is about to update!");
	},
  • 컴포넌트가 DOM안으로 렌더링 되기 전에 호출되는 마지막 메소드
  • 이 메소드 안에서 setState를 호출해도 컴포넌트가 다시 렌더링 되진 않음
    • 즉, render메소드가 다시 호출되어 화면 내용이 갱신된다거나 하진 않는다

11.2.4. render (이후 코드 생략)

  • 모든 컴포넌트에는 이 메소드가 정의되어 있어야 한다
  • 여러 자식 노드를 포함할 수 있는 단 하나의 루트 노드를 리턴하는 책임을 진다
  • 최적화만을 목적으로 하는 경우, 어떤 것도 렌더링하고 싶지 않다면 단순히 null이나 false를 리턴하면 됨

11.2.5. componentDidMount

  • 컴포넌트가 랜더링 되어 DOM에 자리 잡은 직후 호출됨
  • 컴포넌트 생성 완료 여부의 걱정 필요 없이 DOM 질의 작업 수행 가능
  • 모든 준비가 된 컴포넌트에 의존하는 코드라면 모두 이 메소드 안에서 수행하면 됨

 

 11.3. 업데이트단계

상태변경, souldComponentUpdate → componentWillUpdate → render → componentDIdUpdate

 

11.3.1. shouldComponentUpdate

  • 상태가 변경되었어도 컴포넌트 업데이트를 바라지 않을 수도 있음
    • 이와 같은 업데이트 여부를 제어할 수 있게 함
  • true리턴시, 컴포넌트 업데이트
    • false리턴시, 업데이트를 건너띔
shouldComponentUpdate: function(newProps, newState) {
	if(newState.id <=2){
		console.log("Component should upate!");
		return true; //업데이트하겠지
		} else{
			console.log("Component should not update!");
			return false;  //건너뛰겠지
	}
}

11.3.2. componentWillUpdate

  • 컴포넌트가 업데이트되기 직전에 호출
  • 이 메소드 안의 this.setSTate를 통해 상태를 변경할 수 없음

11.3.3. render

  • sholdComponentUpdate가 false를 리턴함으로써 업데이트 작업을 건너뛰지 않았다면 render메소드가 호출됨 ( 컴포넌트가 제대로 보이겠지)

11.3.4. componentDidUpdate

  • 컴포넌트가 업데이트되고 render메소드가 실행이 끝난 뒤에 호출됨
  • 업데이트 이후 수행하고 싶은 코드가 있따면 이 메소드가 가장 적합한 장소임

 11.4. 언마운트 단계

: 컴포넌트 소멸, DOM에서 제거됨

11.4.1. componentWillUnmonut 호출됨

  • 이벤트 리스너 제거, 타이머 중단 등의 뒷정리 작업 가능

12. DOM API 이용하기


 12.1. DOM API 이용하기

  • 리액트는 HTMl엘리먼트에 대한 DOM API에 접근할 수 있는 ref를 제공한다

12.1.1. ref

  • JSX코드: 단순히 DOM을 어떻게 보여야 할지를 기술함
  • ref(refernce): DOM에서 최종 HTML엘리먼트와 JSX사이를 연결

12.1.2. ref작동원리

  • 일반적으로 ref 속성 값에는 js 콜백 함수를 설정함
  • 현재 컴포넌트의 마운트가 끝나면 자동 호출됨
  • 컴포넌트 안의 어디서든 this._input을 이용해 input 엘리먼트를 나타내는 html에 접근할 수 있다