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에 접근할 수 있다