1. Fragments
1) 필요성
JSX 제한 사항
1
2
3
4
5
// root요소는 1개여야 한다.
return (
<h2>Root</h2>
<p>ERROR!!!</p>
)
- 자바스크립트는 둘 이상을 반환할 수 없기 때문이다.
- 즉, 단 1개의 리액트 createElement 호출만 반환되어야 한다.
해결방안1
div로 묶기
1
2
3
4
5
6
7
// root요소는 1개여야 한다.
return (
<div>
<h2>Root</h2>
<p>ERROR!!!</p>
</div>
)
- 그러나
div
지옥이 발생할 수 있다는 문제가 있다.
해결방안2
배열 사용하기
1
2
3
4
5
6
7
// 일반적으로 사용되지 않는 방법이다.
return (
[
<h2 key="h2">Root</h2>
<p key="p">ERROR!!!</p>
]
)
2) wrapper
wrapper 컴포넌트를 추가하여 위에서 언급된 문제를 해결할 수 있다.
1
2
3
4
5
6
7
// 빈 컴포넌트 이다.
// props.children을 반환한다.
const Wrapper = props => {
return props.children;
};
export default Wrapper;
- JSX 요구사항을 충족하기 위해 있는
div
태그의 대안으로 사용할 수 있다. - 빈컴포넌트이기 때문에 DOM에 아무것도 렌더링 하지 않는다.
3) Fragmentss
리액트에는 wrapper 컴포넌트인 Fragments 컴포넌트를 제공한다.
1
2
3
4
5
6
7
8
import {Frament} from "react"
return (
<Frament>
<h2 key="h2">Root</h2>
<p key="p">ERROR!!!</p>
</Frament>
)
- Frament를 사용하면 더 깔끔한 코드를 작성할 수 있다.
- 최종 페이지에 불필요한 HTML 요소들이 줄어든다.
2. Portals
vue3의 teleport와 유사한 개념이다.
1) 필요성
1
2
3
4
5
6
7
8
import {Frament} from "react"
return (
<Frament>
<MyModal />
<MyInputForm />
</Frament>
)
- 기본적으로 Modal은 페이지 위에 표시되는 오버레이다.
- 실제 DOM에 표시 될 때, 현재의 구조로는 페이지에 대한 오버레이로 인식하지는 않을 것이다.
- ex) 스크린 리더가 렌더링되는 HTML 코드를 해석할 때 일반적인 오버레이라고 인식하지 않는다.
- HTML문서를 보았을 때, Modal이 해당 페이지에 대한 오버레이인지를 확실히 표현해야 한다.
2) 사용법
해당 컴포넌트의 HTML 내용을 다른 곳으로 포털, 즉 이동시킬 수 있다.
1
2
3
4
5
6
7
8
9
10
import {Frament} from "react"
import {createPortal} from "react-dom"
const MyModal = (props) => {
return(
<Frament>
{createPortal(렌더링 되어야 하는 노드, 렌더링 되어야할 실제 DOM 컨테이너)}
</Frament>
)
}
- 포털에는 두 가지가 필요하다.
- 컴포넌트를 이동시킬 장소
- 컴포넌트에게 그 곳에 포털을 가져야 한다고 알려줘야 한다.
- createPortal 메서드는 2개의 인수를 취한다.
- 렌더링 되어야 하는 리액트 노드
- 포인터 (렌더링 되어야 하는 실제 DOM 컨테이너) =>
document.getElementById()
로 접근할 수 있다.
3. useRef
직접 DOM 요소에 접근하여 작업할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// state 없이 input 다루기
import React, { useState, useRef } from 'react';
import Card from '../UI/Card';
import Button from '../UI/Button';
import Wrapper from '../Helpers/Wrapper';
import classes from './AddUser.module.css';
const AddUser = (props) => {
const nameInputRef = useRef();
const ageInputRef = useRef();
const addUserHandler = (event) => {
event.preventDefault();
const enteredName = nameInputRef.current.value;
const enteredUserAge = ageInputRef.current.value;
props.onAddUser(enteredName, enteredUserAge);
nameInputRef.current.value = '';
ageInputRef.current.value = '';
};
return (
<Wrapper>
<Card className={classes.input}>
<form onSubmit={addUserHandler}>
<label htmlFor="username">Username</label>
<input id="username" type="text" ref={nameInputRef} />
<label htmlFor="age">Age (Years)</label>
<input id="age" type="number" ref={ageInputRef} />
<Button type="submit">Add User</Button>
</form>
</Card>
</Wrapper>
);
};
export default AddUser;
- ref의 반환 값은 객체로
current
속성을 가지고 있다.- current 속성에 연결된 실제 DOM 노드를 가져온다.
- 해당 노드를 조작, 작업할 수 있다.
- 리액트 없이 DOM을 조작할 수 있다.
- ref로 제어되는 컴포넌트는 리액트로
제어되지 않는
컴포넌트라고 부른다.
- ref로 제어되는 컴포넌트는 리액트로
- 주로 노드 참조 목적으로 사용되지만, 컴포넌트 렌더링에 영향을 끼치지 않는 값을 참조하기 위해 사용된다.
- useState와 달리 current 값이 변경되어도 컴포넌트가 다시 렌더링되지 않는다.
- 불필요한 렌더링을 방지하여 성능을 최적화 할 수 있다.