드디어 리액트 입문! 과 동시에 자신감 하락. Vue가 왜 더 쉽다고 하는지를 뼈저리게 느끼는 중. 하지만 결국 흐름은 명확하게 있는 듯하니, 공부하다보면 사랑스러워 지지 않을까 싶다. 오래보아야 사랑스럽다. 리액트도 그렇다. 제발 그래야만 한다.
웹팩 등의 설정 없이 간편하게 리액트를 사용할 수 있는 방법
npx create-react-app tj-app
, npm start
리액트에서는 JSX 사용를 사용하는데 JSX를 사용할 때는
class를 ⇒ className : 이미 예약된 class 예약어가 있기 때문
최상위요소가 하나여야 한다. 다른 DOM으로 감싸거나 React.Fragments를 이용해야 한다.
{ } 을 이용해 데이터사용
function App() { const name = '리액트' return ( <h1\>{ name }</h1>) }
삼항연산자, && 등을 사용
{ showLink && (<h1\>{ name }</h1>) }
: showLink
가 true일 때 h1태그 동작
key를 넣어줘야 최적화 가능
<ul>{ name.map((item) ⇒ ( <li key={item}> { item } </li>))}</ul>
// App.js
import './App.css';
import Logo from './components/Logo';
import Paragraph from './components/Paragraph';
<Logo size={100} /> // string 사용시 "100px", Number 사용시 { }
<Logo /> // 기본값 200 사용됨
<Paragraph>
Edit <code>src/App.js</code> and save to reload.
</Paragraph>
// Logo/index.js
import PropTypes from 'prop-types';
function Logo(props) {
return(
<img src={logo} className="App-logo" alt="logo"
style={{ width: props.size, height: props.size }} />
)
}
Logo.defaultProps = { // 기본값 정의
size: 200,
}
Logo.propTypes = {
size: PropTypes.number
}
export default Logo;
// Paragraph/index.js
import PropTypes from 'prop-types';
function Paragraph({ children, size }) {
return <p style={{ fontSize: size}}>{children}</p>
}
Paragraph.propTypes = {
children: PropTypes.node.isRequired
}
export default Paragraph;
function Logo({ size = 200 })
과 같이 비구조화 할당 사용가능
defaultProps
: 기본값 정의 , propTypes
: 데이터타입 제한
{ children }
: 하위요소 그대로 사용
children: PropTypes.node.isRequired
: 여기서 node
는 jsx같은 element를 받을 수 있는 타입, isRequired
필수로 들어가야 할 요소
import { useState } from "react"
function Counter() {
const handleIncrease = () => {
setCount(count + 1);
}
return(
<div>
{ count }
<button onClick={handleIncrease}>+</button>
</div>
)
}
export default Counter
// App.js
import Counter from './components/Counter'
function App() {
const [totalCount, setTotalCount] = useState(0);
return (
<div>
<Counter />
</div>
const [count, setCount] = useState(0);
여기서 count는 상태, setCoun는: 값을 바꿔주는 함수, useState(0);
훅, 함수 내의 상태를 관리할 수 있음
import { useState } from "react"
import ProtoTypes from 'prop-types'
function Counter({ onIncrease, onDecrease }) {
const [count, setCount] = useState(0)
const handleIncrease = () => {
setCount(count + 1)
if (onIncrease) onIncrease(count + 1) // 검증
}
const handleDecrease = () => {
setCount(count - 1)
if (onDecrease) onDecrease(count - 1)
}
return(
<div>
{ count }
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
</div>
)
}
Counter.protoTypes = {
onDecrease: ProtoTypes.func, // 함수
onIncrease: ProtoTypes.func
}
export default Counter
//App.js
import { useState } from 'react'
import Counter from './components/Counter'
function App() {
const [totalCount, setTotalCount] = useState(0)
return (
<div>
totalCount: {totalCount}
<Counter
onIncrease={(count) => { setTotalCount(totalCount + 1) }}
onDecrease={(count) => { setTotalCount(totalCount - 1) }} />
<Counter
onIncrease={(count) => { setTotalCount(totalCount + 1) }}
onDecrease={(count) => { setTotalCount(totalCount - 1) }} />
</div>
);
}
export default App;
변화를 감지하여 반응하는 Hook
useEffect(() => {
console.log(`Clicked ${count} times.`)
}, [count])
count가 변할 때 마다 console.log 사용됨
count에 아무 값을 넣지 않으면 라이프사이클 시작 시 실행됨
useEffect(() => {
console.log('Component Loaded')
const handleScroll = () => {
console.log(window.scrollY);
};
document.addEventListener("scroll", handleScroll)
return () => document.removeEventListener("scroll", handleScroll)
}, [])
전역 이벤트 사용: document.addEventListener("scroll", handleScroll)
전역 이벤트 사용시 return을 통해 컴포넌트가 끝날 때 이벤트삭제 필수적으로 넣어야 함!
DOM에 직접 접근할 때나 지역변수로 사용할 때 사용한다.
값이 변경될 때마다 렌더링하는 useState와는 달리 값이 변경되더라도 다시 렌더링하지 않는다.
// App.js
import { useRef } from 'react'
import Input from "./components/Input.js"
function App() {
const inputRef = useRef();
return (
<div>
<Input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</div>
);
}
export default App;
// Input.js
import React from "react"
const Input = React.forwardRef((_, inputRef) => {
return (
<>
<input ref={inputRef} />
</>
)
});
export default Input;
inputRef=useRef
와 Input ref={inputRef}
을 이용하여 DOM에 접근하여 Input과 input연결
import { useRef, useState } from "react"
const AutoCounter = () => {
const [count, setCount] = useState(0)
const intervalId = useRef()
const handleStart = () => {
intervalId.current = setInterval(()=>{
setCount((count) => count + 1)
}, 1000);
}
const handleStop = () => {
clearInterval(intervalId.current)
}
return(
<>
<div>{count}</div>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</>
)
}
export default AutoCounter;
// App.js
import AutoCounter from './components/AutoCounter.js';
function App() {
return (
<AutoCounter/>
);
}
export default App;
useRef로 intervarId를 넣어주기 때문에 값이 변하더라도 다시 렌더링하지 않음
/* @jsxImportSource @emotion/react / 없이 사용가능
craco(create-react-app-config-override) 라이브러리 npm i -D @craco/craco
craco.config.js
파일생성 후 @emotion/babel-preset-css-prop
설치
// craco.config.js
module.exports = {
babel: {
"presets": ["@emotion/babel-preset-css-prop"]
}
}
packge.json에서 "start": "react-scripts start"
⇒ "start": "craco start"
수정
"build": "craco build"
, "test": "craco test"
import styled from "@emotion/styled";
const Box = styled.div`
width: 100px;
height: 100px;
background-color: aqua;
`;
export default Box;
// App.js
function App() {
return (
<div>
<div css= {{ width: 200, height: 100, backgroundColor: "black" }} />
<Box />
</div>
);
}
<div css= {{ width: 200, height: 100, backgroundColor: "black" }} />
처럼 인라인 형식으로도 사용가능