리액트에서 html 요소를 선택하고 싶을 때는
document
객체의 querySelector
나 getElement
선택자를 사용하는 것을 지양합니다
(컴포넌트가 HTML로 렌더링 되기 전에는 DOM 요소에 접근이 불가능하기 때문에)
대신 리액트는 자바스크립트 선택자를 대체하는 기능으로 useRef()
라는 함수를 제공하고 있습니다
먼저 예제 코드부터 살펴보겠습니다
import React, { useRef } from 'react';
const Ref = () => {
const input = useRef(null) // { current: null }
console.log(input)
console.log(input)
return (
<>
<input type="text" name="username" id="username" />
<button>Focus</button>
</>
)
}
export default Ref
useRef()
를 사용하면 최초 렌더링이 되었을 때 input에 접근이 가능해집니다
(클래스형 컴포넌트에서 componentDidMount
가 되었을 때)
import React, { useRef, useEffect } from 'react';
useEffect(() => {
console.log(input.current)
},[])
const Ref = () => {
const input = useRef(null) // { current: null }
const handleSubmit = (e) => {
e.preventDefault()
console.log("submit :", input.current)
}
useEffect(() => {
console.log("componentDidMount :", input.current)
},[])
return (
<>
<form onSubmit={handleSubmit}>
<input type="text" name="username" id="username" ref={input} />
<button type="submit">Focus</button>
</form>
</>
)
}
+)
const obj = {name: "username", id: "username"}
return (
<>
<form onSubmit={handleSubmit}>
<input type="text" {...obj} ref={input} />
<button type="submit">Focus</button>
</form>
</>
)
}
const submit = { onSubmit: handleSubmit}
return (
<>
<form {...submit}>
<input type="text" ref={input} />
<button type="submit">Focus</button>
</form>
</>
)
↑ 이런 식의 사용도 가능합니다
여기서 {...obj}
, {...submit}
의 중괄호({}
)는 객체가 아닌,
이 안에서 JSX 문법을 사용하겠다는 선언적 의미입니다
(앞으로 자주 사용게 될 방식이니 잘 알아둡시다)
const App = () => {
return (
<React.Fragment>
<div>Hello</div>
<div>World</div>
</React.Fragment>
)
}
리액트에서 하나의 컴포넌트에 두 개 이상의 엘리먼트를 최상위에 놓는 것은 붉가능합니다
그런데 리액트 컴포넌트를 다루다보면 이러한 엘리먼트들을 감싸는 상위 엘리먼트를 만들고 싶지 않을 경우가 생길 때가 종종 있습니다
이 때, 리액트가 제공하는 Fragment
를 사용하면 DOM에 별도의 노드를 추가하지 않고 여러 자식을 그룹화할 수 있습니다
*리액트가 버전업이 되면서 빈 꺽쇠(<>
, </>
)로 감싸도 똑같이 작동합니다
<>
<div>Hello</div>
<div>World</div>
</>
컴포넌트를 만들다보면 하나의 컴포넌트에서 이벤트를 관리할 함수가 과다해지는 경우가 생깁니다
특히 로그인이나 회원가입 페이지를 만들 때가 그렇습니다
const handleChange = (e) => {
setUserid(e.target.value)
}
const handleChange2 = (e) => {
setUserpw(e.target.value)
}
return (
<>
<form {...submit}>
<input type="text" name="userid" value={userid} onChange={handleChange} />
<input type="text" name="userpw" value={userpw} onChange={handleChange2} />
</form>
</>
);
};
이러한 경우 코드의 낭비를 막기 위해 커스텀 훅을 사용할 수 있습니다
같은 패턴으로 실행될 코드와 상태관리를 따로 함수로 빼서 쓰기 위해 사용합니다
특히 인풋 박스를 다룰 때 자주 사용하게 됩니다
함수 생성
const useInput = (initialValue) => {
const [value, setValue] = useState("")
const Onchange = (e) => {
setValue(e.target.value)
}
return {
value,
onChange,
}
}
export default useInput
불러와서 사용하기
import useInput from "./useInput"
const obj4 = useInput() // {value:"", }
return (
<>
<form {...submit}>
<input type="text" {...obj} ref={input} className={inputStyle.username} />
<input type="text" name="userid" {...obj4} />
<input type="text" name="userpw" {...obj4} />
<button type="submit">Focus</button>
</form>
</>
);
};
*리턴이 컴포넌트가 아닌 객체가 됩니다
예제2
[useInput.jsx]
import React, { useState } from 'react'
const useInput = (initialValue) => {
const [value, setValue] = useState(initialValue)
const onChange = (e) => {
setValue(e.target.value)
}
return {value, onChange}
}
export default useInput
[ref.jsx]
import useInput from "./useInput"
const Ref = () => {
const input = useRef(null); // { current: null }
const obj2 = useInput("hello world1") // { value: 'hello world'}
const obj3 = useInput("hello world2")
const obj4 = useInput("hello world3")
const obj5 = useInput("hello world4")
...
return (
<input type="text" {...obj2} />
<input type="text" {...obj3} />
<input type="text" {...obj4} />
<input type="text" {...obj5} />
)
if (obj2.value === obj3.value) {}
이런 식으로 상태관리를 각각 할 수 있다는 장점
싱글 페이지 어플리케이션에서 여러 CSS 파일을 적용하려면 어떻게 해야 할까요
우선 기존 방식인 url 변경을 통한 재요청은 불가능합니다
이에 대해서는 여러가지 방법론이 존재합니다
가상DOM 태그에 직접 스타일을 먹일 수 있습니다
자주 사용하는 방식은 아닙니다
관련 패키지 설치
npm install style-loader
환경설정
**웹팩 환경 설정**
```js
module: {
rules: [
{
test: /\.jsx?/,
loader: "babel-loader",
options: {
presets: ["@babel/preset-env","@babel/preset-react"]
}
},
// 스타일 로더 추가
{
test: /\.css/,
use: ["style-loader"],
},
],
},
적용하기
```js
import "../public/index.css"
↑ import 문으로 파일을 불러와서 사용해야 합니다
style-loader
를 사용하면 해당 경로의 css 파일을 함께 번들링합니다
(렌더링되었을 때 <head>
안에 <style>
태그가 담기는 형태로 변환됩니다)
단점
<App />
이 가장 나중에 렌더링되기 때문)관련 패키지 설치
npm install css-loader
npm install mini-css-extract-plugin
환경설정
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
plugins: [
...,
new MiniCssExtractPlugin({ filename: "index.css" })
],
// 바벨 관련 설정
module: {
rules: [
...,
{
test: /\.css/,
use: [MiniCssExtractPlugin.loader,"css-loader"],
},
],
},
css-loader
를 사용하면 header
태그 안에 css 태그를 적용할 파일이 연결된 link
태그가 생성됩니다
(css 파일이 하나로 통합되어 index.css
파일 안에 담기는 형태입니다)
단점
[input.module.css]
.username {
width: 300px;
padding: 7px 14px;
border: 1px solid #333;
font-size: 12px;
color: #999;
}
적용
import inputStyle from "../public/input.module.css"
<input type="text" {...obj} ref={input} className={inputStyle.username} />
css 파일을 모듈화해서 필요한 요소에만 import로 불러온 스타일을 적용하는 방식으로 사용합니다
단점
css-in-js
Styled-Component를 사용하려면 별도의 라이브러리를 설치해야 합니다
npm install process
npm install styled-components
환경설정
const webpack = require('webpack')
plugins: [
new HtmlWebpackPlugin({
template: 'index.html', // 원본
filename: 'index.html' // 번들링될 파일 이름
}),
new MiniCssExtractPlugin({ filename: "index.css" }),
new webpack.ProvidePlugin({
process: "process/browser",
})
],
적용
import styled from "styled-components"
// CSS가 적용된 컴포넌트를 만드는 것
const Div = styled.div`
background: ${ props => props.background === "blue" ? "blue" : "red" };
`
const Button = styled.button`
display: inline-block;
width: ${props => props.size + 'px'};
padding: 7px 14px;
justify-content: center;
align-items: center;
background-color: #000;
color: #fff;
font-weight: Bold;
border: none;
outline: none;
&:hover {
background-color: #666;
}
`
const Style = () => {
return (
<>
<Div background="blue">Hello</Div>
<Div>World</Div>
<Button size="300">버튼</Button>
</>
)
}
export default Style
장점:
*vscode 확장프로그램 vscode-styled-components
설치를 추천합니다