📙1장 리액트 시작
- 자바스크립트 라이브러리로 사용자 인터페이스를 만드는데 사용...
- 컴포넌트(component)
- 특정 부분이 어떻게 생길지 정하는 선언체d
- 재사용이 가능한 API로 수많은 기능들을 내장
- 컴포넌트 하나에서 해당 컴포턴트의 생김새와 작동 방식 정의.
- 렌더링
- 사용자 화면에 뷰를 보여주는 것...
1️⃣ 초기 렌더링
- render 함수 : 컴포넌트가 어떻게 생겼는지 정의, 작동 정보 지닌 객체 반환...
render() { ... }
- render 함수 실행 -> 컴포넌트 재귀적으로 렌더링 -> HTML 마크업 만듦 -> 실제 페이지의 DOM 요소 안에 주입 -> 이벤트 적용
2️⃣ 조화 과정(업데이트)
-
컴포넌트는 새로운 데이터를 가지고 render 함수 재호출(전체 UI를 Virtual Dom에 리렌더링)
-
바뀐 부분만 실제 DOM에 업데이트
-
🔥DOM(Document Object Model)
-
🔥Virtual DOM
- DOM 업데이트 추상화한 자바스크립트 객체를 구성하여 사용 -> DOM 처리 횟수 최소화, 효율적 진행
- 업데이트 3가지 절차 (조화과정)
- 리액트는 프레임워크X, 라이브러리O
- 뷰만 신경 쓰는 라이브러리, 기타 기능 직접 구현
- 다른 웹 프레임워크, 라이브러리 혼용 가능
- 설치: Windows
https://nodejs.org/ko/download/
- npm보다 더 빠르고 효율적인 캐시 시스템과 기타 부가 기능 제공
$ npm install --global yarn
- VS Code 설치
https://code.visualstudio.com/Download
- 형상 관리 도구(configuration Management Tool), 프로젝트 버전 관리 및 협업
- 프로젝트 작업 환경 구축해 주는 도구
$ yarn init
$ yarn create react-app hello-react #yarn create react-app <프로젝트 이름>
- 리액트 개발 전용 서버 구동
$ cd hello-start
$ yarn start #또는 npm start
📙2장 JSX
- 자바스크립트의 확장 문법, XML과 유사
- 브라우저가 실행되기 전에 코드가 번들링되는 과정에서 바벨을 사용해 일반 자바스크립트 형태의 코드로 변환
- 바벨(Babel) : 다양한 종류의 버전의 브라우저에서 사용 가능하도록 코드 변환기
- 컴포넌트에 여러 요소가 있다면 반드시 부모 요소 하나로 감싸야 한다.
- 컴포넌트 내부에 하나의 DOM 트리 구조 -> DOM에서 컴포넌트 변화 감지에 효율적 비교를 위해
function App() {
return (
<div>
<h1>리액트 안녕?</h1>
<h2>잘 작동하니?</h2>
</div>
);
}
- 자바스크립트 표현식 작성 가능 : {}로 감싸기
function App() {
const name='리액트';
return (
<div>
<h1>{name} 안녕?</h1>
<h2>잘 작동하니?</h2>
</div>
);
}
📌ES6의 cosnt와 let
var
: scope이 함수 단위 -> 함수를 빠져나오면 값이 달라짐
💡 let
과 const
는 scope이 블록 단위, 같은 블록 내부에서 중복 선언 불가능
const
: 한번 지정하고 나면 변경이 불가능한 상수를 선언할 때 사용하는 키워드let
: 동적인 값을 담을 수 있는 변수를 선언할 때 사용하는 키워드
- JSX 내부의 자바스크립트 표현식에서 if 문 사용X
➡ JSX 밖에서 if문을 사용해 사전에 값을 설정, {}안에 조건부 연산자(삼항 연산자) 사용 가능
function App() {
const name='리액트';
return (
<div>
{name==='리액트'?(
<h1>리액트입니다.</h1>
):(
<h2>리액트가 아닙니다.</h2>
)}
</div>
);
}
- 특정 조건 만족할 때만 렌더링
- && 가능한 이유 : 리액트에서 false 렌더링할 때는 null과 동일하게 나타나지X
- 0은 예외적으로 나타남
function App() {
const name='리웩트';
return <div>{name==='리액트' && <h1>리액트입니다.</h1>}</div>;
}
- 어떤 값이 undefined인 경우
- OR(||)연산자 사용
- JSX 내부에서 undefined 렌더링은 가능
function App() {
const name=undefined;
return <div>{name||'리액트'}</div>;
}
- DOM 요소에 스타일 적용할 때는 객체 형태로 넣어주어야 함
function App() {
const name='리액트';
return(
<div
style={{
//background-color는 backgroundColor와 같이 -가 사라지고 카멜 표기법으로 작성
backgroundColor:'black',
color:'aqua',
fontSize:'48px', //font-size -> fontSize
fontWeight:'bold', //font-weight -> fontWeight
padding:16 //단위를 생략하면 px로 지정
}}
>{name}</div>
)
}
/* App.css */
.react{
background: aqua;
color: black;
font-size: 48px;
font-weight: bold;
padding: 16px;
}
//App.js
function App() {
const name='리액트';
return(
<div className='react'>{name}</div>
)
}
{/* ... */}
형식으로 작성
function App() {
const name='리액트';
return(
<>
{/* 주석은 이렇게 작성합니다. */}
<div
className='react' //시작 태그를 여러 줄로 작성하면 여기에 주석 작성 가능
>{name}</div>
//하지만 이런 주석이나
/* 이런 주석은 페이지에 그대로 나타나게 됩니다. */
<input/>
</>
)
}
📙3장 컴포넌트
- 컴포넌트 선언 방식 : 함수 컴포넌트, 클래스형 컴포넌트
🔥 함수 컴포넌트
- 선언이 편함, 메모리 자원 적게 사용
🔥 클래스형 컴포넌트
- state 기능 및 라이프 사이클 기능, 임의 메서드 정의 가능
import { Component } from "react";
import "./App.css";
class App extends Component {
render(){
const name='react';
return<div className='react'>{name}</div>;
}
}
export default App;
📌ES6의 클래스 문법
- prototype 대신 class 사용 가능
class Dog{
constructor(name){
this.name=name;
}
say(){
console.log(this.name+': 멍멍');
}
}
const dog=new Dog('흰둥이');
dog.say(); //흰둥이: 멍멍
const MyComponent=()=>{
return <div>나의 새롭고 멋진 컴포넌트</div>;
}
export default MyComponent;
📌ES6의 화살표 함수
- ES6 문법에서 함수를 표현하는 새로운 방식, 함수를 파라미터로 전달할 때 유용
- 값을 연산하여 바로 반환할 때 가독성 높음
- 함수 컴포넌트 선언할 때, function 키워드와 화살표 함수 문법 큰 차이X
setTimeout(function(){
console.log('hello world');
},1000);
setTimeout(()=>{
console.log('hello world');
},1000);
- 모듈 내보내기(export)
- 다른 파일에서 이 파일을 import 할 때, MyComponent 클래스 불러오도록 설정
export default MyComponent;
- 모듈 불러오기(import)
- App 컴포넌트에서 MyComponent 컴포넌트 불러와서 사용
//App.js
import MyComponent from "./MyComponent"
const App=()=>{
return <MyComponent/>;
};
export default App;
- properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소
- props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정 가능
- props 값은 컴포넌트 함수의 파라미터로 받아와 사용 가능
- JSX 내부에서 {} 기호로 감싸줌
const MyComponent = (props) => {
return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};
export default MyComponent;
//App.js
import MyComponent from "./MyComponent";
const App = () => {
return <MyComponent name="React" />;
};
export default App;
const MyComponent = (props) => {
return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};
MyComponent.defaultProps = {
name: "기본 이름",
};
export default MyComponent;
- 리액트 컴포넌트 태그 사이의 내용을 보여주는
props.children
//App.js
import MyComponent from "./MyComponent";
const App = () => {
return <MyComponent>리액트</MyComponent>;
};
export default App;
const MyComponent = (props) => {
return (
<div>
안녕하세요, 제 이름은 {props.name}입니다.
<br />
children 값은 {props.children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: "기본 이름",
};
export default MyComponent;
- ES6의 비구조화 할당 : 객체에서 값을 추출하는 문법
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다.
<br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: "기본 이름",
};
export default MyComponent;
🔥propTypes를 통한 props 검증
- 컴포넌트의 필수 props를 지정하거나 props의 타입(type) 지정하는 경우 propTypes 사용
import PropTypes from "prop-types";
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다.
<br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: "기본 이름",
};
MyComponent.propTypes = {
name: PropTypes.string,
};
export default MyComponent;
🔥 isRequired를 사용하여 필수 propTypes 설정
- propTypes를 지정하지 않았을 때 경고 메시지 띄워줌
import PropTypes from "prop-types";
const MyComponent = ({ name, favoriteNumber, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다.
<br />
children 값은 {children}
입니다.
<br />
제가 좋아하는 숫자는 {favoriteNumber}입니다.
</div>
);
};
MyComponent.defaultProps = {
name: "기본 이름",
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired,
};
export default MyComponent;
- render 함수에서 this.props 조회
- defaultProps, propTypes 방식 동일
import PropTypes from "prop-types";
import { Component } from "react";
class MyComponent extends Component {
render(){
const {name, favoriteNumber,children}=this.props; //비구조화 할당
return (
<div>
안녕하세요, 제 이름은 {name}입니다.
<br />
children 값은 {children}
입니다.
<br />
제가 좋아하는 숫자는 {favoriteNumber}입니다.
</div>
);
}
}
MyComponent.defaultProps = {
name: "기본 이름",
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired,
};
export default MyComponent;
- 컴포넌트 내부에서 바뀔 수 있는 값
- 클래스형 컴포넌트가 지니고 있는 state, 함수 컴포넌트에서 useState라는 함수를 통해 사용하는 state
constructor
: 컴포넌트의 생성자 메서드, 반드시super(props)
호출!, state 초깃값 지정 위함- 컴포넌트의 state는 객체 형식
import { Component } from "react";
class Counter extends Component {
constructor(props) {
super(props);
//state의 초깃값 설정하기
this.state = {
number: 0,
};
}
render() {
const { number } = this.state; //state를 조회할 때는 this.state로 조회
return (
<div>
<h1>{number}</h1>
<button
//onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정
onClick={() => { //이벤트로 설정할 함수를 넣어 줄 때는 화살표 함수 문법!
//this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
//App.js
import Counter from "./Counter";
const App = () => {
return <Counter />;
};
export default App;
🔥 state 객체 안에 여러 값이 있을 때
- state 안에 fixedNumber값 추가
import { Component } from "react";
class Counter extends Component {
constructor(props) {
super(props);
//state의 초깃값 설정하기
this.state = {
number: 0,
fixedNumber: 0,
};
}
render() {
const { number, fixedNumber } = this.state; //state를 조회할 때는 this.state로 조회
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않는 값: {fixedNumber}</h2>
<button
//onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정
onClick={() => {
//this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
🔥 state를 constructor에서 꺼내기
- constructor 메서드 선언하지 않고 state의 초깃값 설정 가능
import { Component } from "react";
class Counter extends Component {
state = {
number: 0,
fixedNumber: 0,
};
🔥 this.setState에 객체 대신 함수 인자 전달하기
prevState
:기존 상태,props
: 현재 지니고 있는 props, 생략 가능
this.setState((prevState,props) => {
return {
// 업데이트하고 싶은 내용
};
});
- 화살표 함수에서 바로 객체로 반환
onClick={() => {
this.setState((prevState) => {
return {
number: prevState.number + 1,
};
});
//위 코드와 아래 코드는 완전히 똑같은 기능
//아래 코드는 함수에서 바로 객체를 반환한다는 의미
this.setState((prevState) => ({
number: prevState.number + 1,
}));
}}
🔥 this.setState가 끝난 후 특정 작업 실행하기
- setState의 두 번째 파라미터인 콜백(callback) 함수 사용
onClick={() => {
this.setState(
{
number: number + 1,
},
() => {
console.log("방금 setState가 호출되었습니다.");
console.log(this.state);
}
);
}}
🔥 배열 비구조화 할당
- 객체 비구조화 할당과 비슷, 배열 안에 들어 있는 값을 쉽게 추출하기 위한 문법
const array=[1,2];
const [one,two]=array;
🔥 useState 사용하기
- useState 함수 인자에는 상태의 초깃값 넣어줌(값 형태 자유)
- 함수 호출 -> 배열 반환 [현재 상태,상태를 바꾸어 주는 함수(Setter 함수)]
import { useState } from "react";
const Say = () => {
const [message, setMessage] = useState(""); //"" : 초깃값
//[현재 상태,상태를 바꾸어 주는 함수(Setter 함수)]
const onClickEnter = () => setMessage("안녕하세요!");
const onClickLeave = () => setMessage("안녕히 가세요!");
return (
<div>
<button onClick={onClickEnter}>입장</button>
<button onClick={onClickLeave}>퇴장</button>
<h1>{message}</h1>
</div>
);
};
export default Say;
//App.js
import Say from "./Say";
const App = () => {
return <Say />;
};
export default App;
🔥 한 컴포넌트에 useState 여러 번 사용하기
import { useState } from "react";
const Say = () => {
const [message, setMessage] = useState("");
const onClickEnter = () => setMessage("안녕하세요!");
const onClickLeave = () => setMessage("안녕히 가세요!");
const [color, setColor] = useState("black");
return (
<div>
<button onClick={onClickEnter}>입장</button>
<button onClick={onClickLeave}>퇴장</button>
<h1 style={{ color }}>{message}</h1>
<button style={{ color: "red" }} onClick={() => setColor("red")}>
빨간색
</button>
<button style={{ color: "green" }} onClick={() => setColor("green")}>
초록색
</button>
<button style={{ color: "blue" }} onClick={() => setColor("blue")}>
파란색
</button>
</div>
);
};
export default Say;
- state 값을 변경할 때
- setState 사용(클래스형 컴포넌트)
- useState를 통해 전달받은 세터 함수 사용 (함수 컴포넌트)
📙6장 컴포넌트 반복
- 반복되는 컴포넌트 렌더링에 사용
- 파라미터로 전달된 함수를 사용해서 배열 내 각 요소를 원하는 규칙에 따라 변환한 후 그 결과로 새로운 배열 생성
arr.map(callback,[thisArg])
- callback: 새로운 배열의 요소를 생성하는 함수, 파라미터 3가지
- currentValue: 현재 처리 요소
- index: 현재 처리 요소 index 값
- array: 현재 처리 원본 배열
- thisArg(선택 항목): callback 함수 내부에서 사용할 this 레퍼런스
const numbers=[1,2,3,4,5];
const result=numbers.map(num=>num*num);
console.log(result);
const IterationSample = () => {
const names = ["눈사람", "얼음", "눈", "바람"];
const nameList = names.map((name) => <li>{name}</li>);
return <ul>{nameList}</ul>;
};
export default IterationSample;
//App.js
import { Component } from "react";
import IterationSample from "./IterationSample"; //IterationSample 컴포넌트 불러와 렌더링
class App extends Component {
render() {
return <IterationSample />;
}
}
export default App;
- 데이터가 가진 고유의 값으로 설정
- map 함수의 인자로 전달되는 함수 내부에서 컴포넌트 props 설정하듯 설정
- Virtual Dom 비교하는 과정에서 key로 바로 변화 감지 가능
const IterationSample = () => {
const names = ["눈사람", "얼음", "눈", "바람"];
const nameList = names.map((name, index) => <li key={index}>{name}</li>);
//고유의 값이 없을 때만 index를 key 값으로 설정
return <ul>{nameList}</ul>;
};
export default IterationSample;
- useState를 사용해 상태 설정
- 3가지 상태 : 데이터 배열, 텍스를 입력 input 상태, 배열에 새로운 항목 추가할 때 사용하는 고유 id 상태
import { useState } from "react";
const IterationSample = () => {
const [names, setNames] = useState([ //해당 객체는 문자열과 고유 id 값이 있음
{ id: 1, text: "눈사람" },
{ id: 2, text: "얼음" },
{ id: 3, text: "눈" },
{ id: 4, text: "바람" },
]);
const [inputText, setInputText] = useState("");
const [nextId, setNextId] = useState(5); //새로운 항목을 추가할 때 사용할 id
const nameList = names.map((name) => <li key={name.id}>{name.text}</li>);
//map함수에서 name.id를 key 값으로 설정
return <ul>{nameList}</ul>;
};
export default IterationSample;
concat
: 배열에 새 항목 추가(새로운 배열을 만들어줌)
import { useState } from "react";
const IterationSample = () => {
const [names, setNames] = useState([
{ id: 1, text: "눈사람" },
{ id: 2, text: "얼음" },
{ id: 3, text: "눈" },
{ id: 4, text: "바람" },
]);
const [inputText, setInputText] = useState("");
const [nextId, setNextId] = useState(5); //새로운 항목을 추가할 때 사용할 id
const onChange = (e) => setInputText(e.target.value);
const onClick = () => {
const nextNames = names.concat({
id: nextId, //nextId 값을 id로 설정하고
text: inputText,
});
setNextId(nextId + 1); //nextId 값에 1을 더해준다
setNames(nextNames); //names 값을 업데이트
setInputText(""); //inputText를 지운다
};
const nameList = names.map((name) => <li key={name.id}>{name.text}</li>);
//name.id를 key 값으로 설정
return (
<>
<input value={inputText} onChange={onChange} />
<button onClick={onClick}>추가</button>
<ul>{nameList}</ul>
</>
);
};
export default IterationSample;
- 더블클릭(
onDoubleClick
) -> 해당 항목 삭제 fiter
: 불변성 유지하면서 배열의 특정 항목 삭제할 때 쓰는 배열의 내장함수
import { useState } from "react";
const IterationSample = () => {
const [names, setNames] = useState([
{ id: 1, text: "눈사람" },
{ id: 2, text: "얼음" },
{ id: 3, text: "눈" },
{ id: 4, text: "바람" },
]);
const [inputText, setInputText] = useState("");
const [nextId, setNextId] = useState(5); //새로운 항목을 추가할 때 사용할 id
const onChange = (e) => setInputText(e.target.value);
const onClick = () => {
const nextNames = names.concat({
id: nextId, //nextId 값을 id로 설정하고
text: inputText,
});
setNextId(nextId + 1); //nextId 값에 1을 더해준다
setNames(nextNames); //names 값을 업데이트
setInputText(""); //inputText를 지운다
};
const onRemove = (id) => {
//onRemove 함수를 만들어 각 li 요소 이벤트 등록
const nextNames = names.filter((name) => name.id !== id);
//name.id ===id인 항목만 삭제
setNames(nextNames);
};
const nameList = names.map((name) => (
<li key={name.id} onDoubleClick={() => onRemove(name.id)}>
{name.text}
</li>
));
//name.id를 key 값으로 설정
return (
<>
<input value={inputText} onChange={onChange} />
<button onClick={onClick}>추가</button>
<ul>{nameList}</ul>
</>
);
};
export default IterationSample;
📙8장 Hooks
- 가장 기본적인 Hook, 함수 컴포넌트에서도 가변적인 상태 지닐 수 있음
- 하나의 useState 함수는 하나의 상태 값만 관리 -> 컴포넌트에서 관리해야 할 상태 여러개면 여러개 useState 사용
- 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook
import { useState, useEffect } from "react";
const Info = () => {
const [name, setName] = useState("");
const [nickname, setNickname] = useState("");
useEffect(() => {
console.log("렌더링이 완료되었습니다!");
console.log({
name,
nickname,
});
});
const onChangeName = (e) => {
setName(e.target.value);
};
const onChangeNickname = (e) => {
setNickname(e.target.value);
};
return (
<div>
<div>
<input value={name} onChange={onChangeName} />
<input value={nickname} onChange={onChangeNickname} />
</div>
<div>
<b>이름:</b> {name}
</div>
<div>
<b>닉네임:</b> {nickname}
</div>
</div>
);
};
export default Info;
import Info from "./Info";
import "./App.css";
const App = () => {
return <Info />;
};
export default App;
- useEffect에서 설정한 함수를 컴포넌트가 화면에 맨 처음 렌더링될 때만 실행, 업데이트될 때는 실행X
➡ 함수의 두 번째 파라미터로 비어 있는 배열 넣어줌
useEffect(() => {
console.log("렌더링이 완료되었습니다!");
}, []);
- useEffect의 두 번째 파라미터로 전달되는 배열 안에 검사하고 싶은 값 넣어줌
useEffect(() => {
console.log("렌더링이 완료되었습니다!");
}, [name]);
- useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트할 때 사용하는 Hook
- 현재 상태, 업데이트르 위해 필요한 정보를 담은 액션(action) 값을 전달받아 새로운 상태를 반환하는 함수
- 불변성 지켜줘야함!
function reducer(state,action){
return{...}; //불변성을 지키면서 업데이트한 새로운 상태를 반환
}
//액션 값 : useReducer의 액션은 꼭 type 지닐 필요X, 객체가 아니라 문자열, 숫자 상관X
{
type:'INCREMENT',
//다른 값들이 필요하다면 추가로 들어감
}
useReducer(리듀서 함수, 해당 리듀서의 기본값)
- 장점: 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있음
import { useReducer } from "react";
function reducer(state, action) {
//action.type에 따라 다른 작업 수행
switch (action.type) {
case "INCREMENT":
return { value: state.value + 1 };
case "DECREMENT":
return { value: state.value - 1 };
default:
//아무것도 해당되지 않을 때 기존 상태 반환
return state;
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, { value: 0 });
//state: 현재 가리키고 있는 상태, dispatch: 액션 발생시키는 함수
//dispatch(action) => 리듀서 함수 호출
//useReducer(리듀서 함수, 해당 리듀서의 기본값)
return (
<div>
<p>
현재 카운터 값은 <b>{state.value}</b>입니다.
</p>
<button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
</div>
);
};
export default Counter;
- setState를 해준 것과 유사한 방식으로 처리 가능
- 인풋의 개수가 많아져도 코드를 짧고 깔끔하게 유지 가능
import { useReducer } from "react";
function reducer(state, action) {
return {
...state,
[action.name]: action.value,
};
}
const Info = () => {
const [state, dispatch] = useReducer(reducer, {
name: "",
nickname: "",
});
const { name, nickname } = state;
//이벤트 객체가 지니고 있는 e.target 값 자체를 액션 값으로 사용
const onChange = (e) => {
dispatch(e.target);
};
return (
<div>
<div>
{/* input 태그에 name 값을 할당하고 e.target.name을 참조해 setState와 유사하게 처리 */}
<input name="name" value={name} onChange={onChange} />
<input name="nickname" value={nickname} onChange={onChange} />
</div>
<div>
<b>이름:</b> {name}
</div>
<div>
<b>닉네임:</b> {nickname}
</div>
</div>
);
};
export default Info;
- 함수 컴포넌트 내부에서 발생하는 연산 최적화
- 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용하는 방식
import { useState, useMemo } from "react";
const getAverage = (numbers) => {
console.log("평균값 계산 중...");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = (e) => {
setNumber(e.target.value);
};
const onInsert = () => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
};
const avg = useMemo(() => getAverage(list), [list]);
//list 배열의 내용이 바뀔 때만 getAverge 함수 호출
return (
<div>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b>
{avg}
</div>
</div>
);
};
export default Average;
- useMemo와 비슷, 렌더링 성능 최적화 상황에 사용
- 만들어 놨던 함수 재사용 가능
useCallback(생성하고 싶은 함수, 배열(어떤 값이 바뀌었을 때 함수를 새로 생성하는지 명시))
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []); //컴포넌트가 처음 렌더링될 때만 함수 생성, 렌더링될 때 만들었던 함수 재사용
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
}, [number, list]); //number 혹은 list가 바뀌었을 때만 함수 생성, 아니면 함수
- 함수 컴포넌트에서 ref를 쉽게 사용 가능하게 함
- useRef를 사용해 ref 설정 -> useRef를 통해 만든 객체 안의 current 값이 실제 엘리먼트 가리킴
import { useState, useMemo, useCallback, useRef } from "react";
const getAverage = (numbers) => {
console.log("평균값 계산 중...");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
//ref를 위한 변수 선언
const inputEl = useRef(null);
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []); //컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
inputEl.current.focus(); //커서 이동을 원할 때,
}, [number, list]); //number 혹은 list가 바뀌었을 때만 함수 생성
const avg = useMemo(() => getAverage(list), [list]);
//list 배열의 내용이 바뀔 때만 getAverge 함수 호출
return (
<div>
<input value={number} onChange={onChange} ref={inputEl} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b>
{avg}
</div>
</div>
);
};
export default Average;
📌ref통한 input 태그 커서/포커스 조정
ref
: DOM에 직접 접근해야할 때 사용- 직접 접근이 필요한 경우
- input / textare 등에 커서 조정
- DOM 의 크기를 가져와야 할 때
- DOM 에서 스크롤 위치를 가져오거나 설정을 해야 할 때
- 외부 라이브러리 (플레이어, 차트, 캐로절 등)을 사용 할 때
- 로컬 변수 : 렌더링과 상관없이 바뀔 수 있는 값
- 렌더링과 관련되지 않은 값을 관리할 때 사용, ref 안의 값이 바뀌어도 컴포넌트가 렌더링X
import { useRef } from "react";
const RefSample = () => {
const id = useRef(1);
const setId = (n) => {
id.current = n;
};
const printId = () => {
console.log(id.current);
};
return <div>refsample</div>;
};
export default RefSample;