DOM 엘리먼트 접근
- HTML 엘리먼트의 속성이나 메소드에 직접 접근해야할 일이 생길것이다. 자바스크립트의 DOM API를 통해 직접 HTML 엘리먼트를 다루는 일이 '리액트 방식'으로 할 때보다 더 쉬운 경우가 많기 때문이다.
- 리액트는 HTML 엘리먼트에 대한 DOM API에 접근할 수 있게 하는, ref라는 도구를 제공한다.
- 또한 페이지 안의 어떤 HTML 엘리먼트든 렌더링할 수 있게 하는 포털이라는 기능도 제공한다.
-- 예제를 통해 한번 알아보도록 하자
>> ref 와의 첫만남
- 대부분의 경우 render 메소드 안에서 HTML을 닮은 JSX 코드를 작성해왔다. 리액트는 DOM안에서의 최종 HTML 엘리먼트와 JSX 사이를 연결해주는 ref라는 기능을 제공한다.
- 위의 render 메서를 한번 봐보자
render(){
var squareStyle = {
backgroundColor : this.state.bgColor
}
return (
<div className="colorArea">
<div style={squareStyle} className="colorSquare"></div>
<form onSubmit={this.setNewColor}>
<input onChange={this.colorValue} placeholder="Enter a color value"></input>
<button type="submit">go</button>
</form>
</div>
)
}
- 자바스크립트를 사용해 API를 호출할수 있게, Input 엘리먼트의 DOM에 접근 하는 작업을 해보자
- 접근하려는 HTML엘리먼트에 ref라는 속성을 설정할 수 있다
render(){
var squareStyle = {
backgroundColor : this.state.bgColor
}
return (
<div className="colorArea">
<div style={squareStyle} className="colorSquare"></div>
<form onSubmit={this.setNewColor}>
<input onChange={this.colorValue}
ref= {}
placeholder="Enter a color value">
</input>
<button type="submit">go</button>
</form>
</div>
)
}
- 현재 ref의 속성 값은 비어있다
- 보통은 현재 컴포넌트가 마운트되면 자동으로 호출될 자바스크립트 콜백 함수를 ref 속성 값으로 설정한다.
- ref 속성 값으로 단순히 DOM 엘리먼트의 참조를 저장하는 자바스크립트 함수를 지정한다면 다음과 같을 것이다.
render(){
var squareStyle = {
backgroundColor : this.state.bgColor
}
var self = this;
return (
<div className="colorArea">
<div style={squareStyle} className="colorSquare"></div>
<form onSubmit={this.setNewColor}>
<input onChange={this.colorValue}
ref = {
function(el) {
self._input = el
}
}
placeholder="Enter a color value"></input>
<button type="submit">go</button>
</form>
</div>
)
}
- 이 익명함수는 컴포넌트가 마운트되면 호출되며, 최종 HTML DOM 엘리먼트에 대한 참조 인자로 받는다.
function(el) {
self._input = el
}
- 이 인자를 el 이라고 했지만 어떤 이름이든 상관 없다.
- 이 함수는 단순히 _input이라는 커스텀 속성에 DOM 엘리먼트의 값을 지정하는 일을 한다.
- 또한 컴포넌트에 _input 속성을 만들기 위해 self 변수를 사용해 클로저를 만들었다.
- 지금의 목표는 폼이 제출되면 input 엘리먼트의 콘텐츠를 지우고 포커스를 갖게 하는 것이다
- 이를 위한 코드를 setNewColor에 넣기로하고 다음 내용을 추가하자
setNewColor(e){
this.setState({
bgColor : this.state.color
});
this._input.focus(); --추가
this._input.input.value= ""; --추가
e.preventDefault();
}
- this._input.value = "" 를 호출함으로써 input 엘리먼트에 입력됬던 내용은 삭제될 것이다.
- 또한 this._input.focus() 를 호출해 input 엘리먼트로 포커스가 가게 했다.
- ref 와 관련된 작업은 이 두라인이 전부다.
** ref 와 관련하여 콜백함수를 다룰때 화살표 함수를 사용하면 작업이 더 편해진다.
<input
ref = {
function(el) {
self._input = el;
}
}
>
</input>
또한 self 변수를 this로 초기화하여 컴포넌트에 _input 속성이 확실히 만들어지게 했다.
근데 여기서 화살표 함수를 사용한다면 더 깔끔하게 만들수 있다
<input
ref = {
(el) => this._input = el
}>
</input>
- 화살표 함수는 컨텍스트를 다루기 때문에 함수 안에서 this를 사용해 컨포넌트를 참조할 수 있게
됬다. 따라서 self와 같은 외부 변수를 만들 필요가 없어졌다.
>> 포털 사용하기
- 지금 까지는 단일 컴포넌트든 복수 컴포넌트의 조합이든 오직 JSX가 만드는 결과로서 HTML을 다뤘다. 이는 부모 컴포넌트가 강제하는 DOM 계층 구조로 제한된다는 의미이다. 따라서 다른 위치의 DOM엘리먼트에 임의로 접근하는 일은 불가능해 보인다
- 하지만 JSX는 페이지 어느 곳이든 DOM엘리먼트로 렌더링 시킬 수 있다. 바로 포털이라는 기능이다.
- 포털의 사용방법은 ReactDOM.render 메소드와 매우 유사하다. 즉 렌더링할 JSX를 지정하고, 렌더링해 보낼 대상 DOM 엘리먼트를 지정하면 된다.
- 예제로 돌아가서 container div 엘리먼트와 동등한 수준으로, 즉 형제 h1 엘리먼트를 추가해 보자
- 현재 보여주고 있는 컬러의 이름으로 h1 엘리먼트의 값이 바뀌게 해보자 핵심은 container div 와 h1이 형제 엘리먼트라는 점이다.
- ColorLabel 이라는 컴포넌트를 인스턴스화 하고, state.bgColor 속성을 color 속성 값으로 설정했다.
- 또한 ColorLabel이라는 컴포넌트를 정의해 주었다.
- 여기서 중요한 점은 return 구문에서 ReactDOM.createPortal()으 호출 결과를 리턴하는 부분이다.
- ReactDOM.createPortal()은 두개의 인자를 받는다
1) 출력될 내용의 JSX 이며 -> 단지 문자하나와 속성으로부터의 컬러 값이 전부다.
2) 둘쨰는 그 JSX를 출력시킬 대상 DOM 엘리먼트이다. -> 모든 내용을 출력시킬 DOM 엘리먼트는 h1엘리 먼트이다.
- 강조할 사항은 h1 엘리먼트가 container div를 출력하는 앱의 범위 밖에 있다는 점이다.
- 즉 포털을 사용함으로써 부모와 자식으로 이뤄진 기존 계층 구조에 갇히지 않고 페이지의 어느 엘리먼트든 직접 접근이 가능하게 된다는 얘기이다.
-- 최종코드
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Lifecycle Methods</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<style>
#container {
padding : 50px;
background-color : #fff;
}
.colorSquare {
box-shadow : 0px 0px 25px 0px #333;
width : 242px;
height : 242px;
margin-bottom : 15px;
}
.colorArea input{
padding : 10px;
font-size : 16px;
border : 2px solid #ccc;
}
.colorArea button{
padding : 10px;
font-size : 16px;
margin : 10px;
background-color : #666;
color : #fff;
border : 2px solid #666;
}
.colorArea button:hover{
background-color : #111;
border-color : #111;
cursor : pointer;
}
#colorHeading {
padding : 0;
margin : 50px;
margin-bottom : -20px;
font-family: sans-serif;
}
</style>
</head>
<body>
<h1 id="colorHeading"> Colorizer</h1>
<div id="container"></div>
<script type="text/babel">
var heading = document.querySelector("#colorHeading");
class ColorLabel extends React.Component{
render(){
return ReactDOM.createPortal(
": " + this.props.color,
heading
);
}
}
class Colorizer extends React.Component{
constructor(props){
super(props);
this.state ={
color : "",
bgColor : "white"
};
this.colorValue = this.colorValue.bind(this);
this.setNewColor = this.setNewColor.bind(this);
}
colorValue(e){
this.setState({
color : e.target.value
});
}
setNewColor(e){
this.setState({
bgColor : this.state.color
})
this._input.focus();
this._input.value="";
e.preventDefault();
}
render(){
var squareStyle = {
backgroundColor : this.state.bgColor
}
return (
<div className="colorArea">
<div style={squareStyle} className="colorSquare"></div>
<form onSubmit={this.setNewColor}>
<input onChange={this.colorValue}
ref = {
(el) => this._input = el
}
placeholder="Enter a color value"></input>
<button type="submit">go</button>
</form>
<ColorLabel color={this.state.bgColor}/>
</div>
)
}
}
ReactDOM.render(
<div>
<Colorizer />
</div>
,document.querySelector("#container")
);
</script>
</body>
</html>
'dev > React' 카테고리의 다른 글
[React18] 엘리먼트 렌더링 (0) | 2022.12.11 |
---|---|
[React] 외부 데이터 사용 (0) | 2022.12.02 |
[REACT] 컴포넌트 생명주기 (0) | 2022.12.01 |
[React] 이벤트 (0) | 2022.11.30 |
[React] 데이터에서 UI로 (0) | 2022.11.30 |