수강하기 ---> 처음 만난 리액트(React)
강의를 책으로도 볼 수 있도록
강의 내용을 고스란히 책에 담았습니다.
동영상 강의와 함께 책을 보면서
리액트의 세계로 빠져보세요!
소문난 명강의
소플의 처음 만난 리액트
- 리액트 기초 개념 정리부터 실습까지
vscode 확장 패키지 추가
1. - Auto Import - ES6, TS, JSX, TSX
2. - Reactjs code snippets
3. - ESLint
4. - Prettier - Code formatter
Visual Studio Code 폴더/파일 아이콘 변경하기
리액트 프로젝트 생성
npx create-react-app 경로
예) npx create-react-app E:\react-app2
npx create-react-app my-app
#애플리케이션 실행
실행 : npm start
1. JSX
리액트 문서 -> JSX 소개
- JSX(JavaScript XML)는 Javascript에 XML과 HTML 추가한 확장한 문법이다. - JSX는 리액트로 프로젝트를 개발할 때 사용되므로 공식적인 자바스크립트 문법은 아니다. - 브라우저에서 실행하기 전에 바벨을 사용하여 일반 자바스크립트 형태의 코드로 변환된다.
const element = Hello, world!;
위에 희한한 태그 문법은 문자열도, HTML도 아닙니다.
JSX라 하며 JavaScript를 확장한 문법입니다. UI가 어떻게 생겨야 하는지 설명하기 위해 React와 함께 사용할 것을 권장합니다. JSX라고 하면 템플릿 언어가 떠오를 수도 있지만, JavaScript의 모든 기능이 포함되어 있습니다.
JSX는 React “엘리먼트(element)” 를 생성합니다. 다음 섹션에서는 DOM에 어떻게 렌더링하는지 알아보겠습니다. 아래를 보면 JSX를 시작하기 위해 필요한 기본사항을 찾으실 수 있습니다.
JSX 속성 정의
어트리뷰트에 따옴표를 이용해 문자열 리터럴을 정의할 수 있습니다.
const element = link ;
중괄호를 사용하여 어트리뷰트에 JavaScript 표현식을 삽입할 수도 있습니다.
const element = ;
어트리뷰트에 JavaScript 표현식을 삽입할 때 중괄호 주변에 따옴표를 입력하지 마세요. 따옴표(문자열 값에 사용) 또는 중괄호(표현식에 사용) 중 하나만 사용하고, 동일한 어트리뷰트에 두 가지를 동시에 사용하면 안 됩니다.
Book.jsx
import React from 'react'; const Book = (props) => { return ( {`이 책의 이름음 ${props.name} 입니다.`} {`이 책은 총 ${props.numOfPage} 페이지로 이루어져 있습니다`}); }; export default Book;
Library.jsx
import React from 'react'; import Book from './Book'; const Library = () => { return ( ); }; export default Library;
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import Library from './chapter_03/Library'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( {/* */} ); reportWebVitals();
2.Rendering Elements
리액트 문서 : https://ko.reactjs.org/docs/rendering-elements.html
DOM에 엘리먼트 렌더링하기
HTML 파일 어딘가에
이 안에 들어가는 모든 엘리먼트를 React DOM에서 관리하기 때문에 이것을 “루트(root)” DOM 노드라고 부릅니다.
React로 구현된 애플리케이션은 일반적으로 하나의 루트 DOM 노드가 있습니다. React를 기존 앱에 통합하려는 경우 원하는 만큼 많은 수의 독립된 루트 DOM 노드가 있을 수 있습니다.
React 엘리먼트를 루트 DOM 노드에 렌더링하려면 둘 다 ReactDOM.render()로 전달하면 됩니다.
const element = Hello, world ; ReactDOM.render(element, document.getElementById('root'));
위 코드를 실행하면 화면에 “Hello, world”가 보일 겁니다.
리액트(React) Elements의 특징 및 렌더링하기
React elements are immutable
Elements 생성 후에는 children이나 attributes를 바꿀 수 없다!
Elements를 생성한 이후에 실제로 화면에 보여주기 위해서는 렌더링 과정을 거쳐야 한다
React elements가 렌더링 되는 과정은 virtual DOM에서 실제 DOM으로 이동하는 과정
렌더링 된 Elements를 업데이트 하기
React element의 중요한 특징인 immutable 때문에 element를 업데이트하기 위해서는 다시 생성해야 한다
시계만들기
https://codepen.io/gaearon/pen/gwoJeZ?editors=1010
Clock.jsx
import React, { useEffect, useState } from 'react'; const Clock = (props) => { const [dateTime, setDateTime] = useState(new Date().toLocaleTimeString()); useEffect(() => { setInterval(() => { setDateTime(new Date().toLocaleTimeString()); }, 1000); }, [dateTime]); return ( 안녕, 리액트! 현재 시간 {dateTime } ); }; export default Clock;
3.Components and Props
https://ko.reactjs.org/docs/components-and-props.html
Components와 Props
컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있습니다. 이 페이지에서는 컴포넌트의 개념을 소개합니다. 자세한 컴포넌트 API 레퍼런스는 여기에서 확인할 수 있습니다.
개념적으로 컴포넌트는 JavaScript 함수와 유사합니다. “props”라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환합니다.
함수 컴포넌트와 클래스 컴포넌트
컴포넌트를 정의하는 가장 간단한 방법은 JavaScript 함수를 작성하는 것입니다.
function Welcome(props) { return Hello, {props.name} ; }
이 함수는 데이터를 가진 하나의 “props” (props는 속성을 나타내는 데이터입니다) 객체 인자를 받은 후 React 엘리먼트를 반환하므로 유효한 React 컴포넌트입니다. 이러한 컴포넌트는 JavaScript 함수이기 때문에 말 그대로 “함수 컴포넌트”라고 호칭합니다.
또한 ES6 class를 사용하여 컴포넌트를 정의할 수 있습니다.
class Welcome extends React.Component { render() { return Hello, {this.props.name} ; } }
React의 관점에서 볼 때 위 두 가지 유형의 컴포넌트는 동일합니다.
class는 몇 가지 추가 기능이 있으며 이에 대해서는 다음 장에서 설명합니다. 그때까지는 간결성을 위해 함수 컴포넌트를 사용하겠습니다. 함수 컴포넌트와 클래스 컴포넌트 둘 다 몇 가지 추가 기능이 있으며 이에 대해서는 다음 장에서 설명합니다.
컴포넌트는 대문자로 시작해야 한다.
1) 함수형 컴포넌트 (Stateless Functional Component)
- 가장 간단하게 컴포넌트를 정의하는 방법은 자바스크립트 함수를 작성하는 것이다.
- 하기 예시의 export는 구문은
작성한 MyComponent 파일을 다른 파일에서 import 할때 MyComponent로 불러올 수 있도록 정의해 주는 부분이다.
- 여기서 import시 js, jsx 등 파일 확장자를 생략해도 자동으로 찾을 수 있다. 이는 "웹팩 코드 검색 확장자(webpack module resolution)" 기능 때문이다. 확장자가 파일 이름에 없는 경우 웹팩의 확장자 옵션(extentions)에 정의된 확장자 목록을 통해 확장자 이름을 포함한 파일이 있는지 확인하여 자동으로 임포트 한다.
ex) import 'Header'; 의 경우 Header.js > Header.jsx 순서로 확인 한다.
ex) 함수형 컴포넌트 예시
import React from 'react'; function MyComponent(props) { return Hello, {props.name} ; } export default MyComponent; //다른 JS파일에서 불러올 수 있도록 내보내주기
2) 클래스형 컴포넌트 ( Class Component )
- 컴포넌트 구성 요소, 리액트 생명주기를 모두 포함하고 있다. (리액트 생명주기는 따로 조금더 자세히 알아보도록 한다.)
- 프로퍼티, state, 생명주기 함수가 필요한 구조의 컴포넌트를 만들 때 사용한다.
import React from 'react'; class MyComponent extends React.Component { constructor(props) { // 생성함수 super(props); } componentDidMount() { // 상속받은 생명주기 함수 } render() { // 상속받은 화면 출력 함수, 클래스형 컴포넌트는 render() 필수 return Hello, {this.props.name} ; } } export default MyComponent; //다른 JS파일에서 불러올 수 있도록 내보내주기
출처: https://goddaehee.tistory.com/299 [갓대희의 작은공간:티스토리]
Comment.jsx
import React from 'react'; const styles = { wrapper: { margin: 8, padding: 8, display: "flex", flexDirection: "row", border: "1px solid grey", borderRadius:16 }, imageContainer: {}, image: { width: 50, height: 50, borderRadius:25, }, contentContainer: { marginLeft: 8, display: "flex", flexDirection: "column", justifyContent:"center", }, nameText: { color: "black", fontSize: 16, fontWeight:"bold", }, commnetText: { color: "black", fontSize:16, } } const Comment = (props) => { return (
CommentList.jsx
import React from 'react'; import Comment from './Comment'; const comments = [ { name: "이인제", comment:"안녕하세요. 소플입니다" }, { name: "유재석", comment:"리액트 재미있어요!" }, { name: "강민경", comment: "저도 리액트 배워보고 싶어요!" }, ] const CommentList = () => { return ( <div> {comments.map((comment) => { return <Comment name={comment.name} comment={comment.comment} /> })} </div> ); }; export default CommentList;
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import Library from './chapter_03/Library'; import Clock from './chapter_04/Clock'; import CommentList from './chapter_05/CommentList'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( {/* */} {/* */} {/* */} ); reportWebVitals();
4.State and Lifecycle
Mount
컴포넌트가 처음 실행될 때 그것을 Mount라고 표현합니다. 컴포넌트가 시작되면 우선 context, defaultProps와 state를 저장합니다. 그 후에 componentWillMount 메소드를 호출합니다. 그리고 render로 컴포넌트를 DOM에 부착한 후 Mount가 완료된 후 componentDidMount가 호출됩니다.
주의할 점은, componentWillMount에서는 props나 state를 바꾸면 안 됩니다. Mount 중이기 때문이죠. 그리고 아직 DOM에 render하지 않았기 때문에 DOM에도 접근할 수 없습니다.
componentDidMount에서는 DOM에 접근할 수 있습니다. 그래서 여기에서는 주로 AJAX 요청을 하거나, setTimeout, setInterval같은 행동을 합니다.
정리하면 다음 순서로 실행됩니다.
- state, context, defaultProps 저장
- componentWillMount
- render
- componentDidMount
(실습) state 사용하기
Notification.jsx
import React, { Component } from 'react'; const styles = { wrapper: { margin: 8, padding: 8, display: "flex", flexDirection: "row", border: "1px solid grey", borderRadius:16 }, messageText:{ color:"black", fontSize:16 } } class Notification extends Component { constructor(props) { super(props); this.state={} } componentDidMount() { console.log(`${this.props.id} componentDidMount() called.`); } componentDidUpdate() { console.log(`${this.props.id} componentDidUpdate() called.`); } componentWillUnmount() { console.log(`${this.props.id} componentWillUnmount() called.`); } render() { return ( {this.props.message} ); } } export default Notification;
NotificationList.jsx
import React, { Component } from 'react'; import Notification from './Notification'; const reservedNotifications = [ { id:1, message:"안녕하세요. 오늘 일정을 알려드립니다.", }, { id:2, message:"점심식사 시간입니다.", }, { id: 3, message:"이제 곧 미팅이 시작됩니다.", }, ] let timer; class NotificationList extends Component { constructor(props) { super(props); this.state = { notification: [] } } componentDidMount() { const { notification } = this.state; timer = setInterval(() => { if (notification.length < reservedNotifications.length) { const index = notification.length; notification.push(reservedNotifications[index]); this.setState({ notification: notification }); } else { // this.setState({ // notification: [] // }); clearInterval(timer); } }, 1000); } render() { return ( { this.state.notification.map((notification) => { return }) } ); } } export default NotificationList;
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import Library from './chapter_03/Library'; import Clock from './chapter_04/Clock'; import CommentList from './chapter_05/CommentList'; import NotificationList from './chapter_06/NotificationList'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( {/* */} {/* */} {/* */} {/* */} ); reportWebVitals();
두번 출력할 경우 제거후 만 입력
버그가 아니라 개발시 테스트를 위한 엄격 모드서 빌드시에는 정상적으로 출력된다.
출력:
1 componentDidMount() called.
1 componentDidUpdate() called.
2 componentDidMount() called.
1 componentDidUpdate() called.
2 componentDidUpdate() called.
3 componentDidMount() called.
5.Hooks
React Hooks
React Hooks은 리액트의 새로운 기능으로 React 16.8버전에 새로 추가된 기능으로 state, component에 대한 것들을 바꿔놓았다.
예를 들면 function component에서 state을 가질 수 있게 된 것
만일 앱을 react hook을 사용하여 만든다면 class component, render 등을 안해도 된다는 뜻이다.
모든 것은 하나의 function이 되는 것 함수형 프로그래밍이 가능해지는 것
React Hokks를 사용 했을 때와 안했을 때의 차이를 간단한 예제로 살펴보도록 하겠습니다.
class component
import React, { Component } from 'react'; class App extends Component { state = { count: 0, }; countUpdate(n) { this.setState({ count: n, }); } render() { const { count } = this.state; return ({count} { this.countUpdate(count + 1); }} > 증가 ); } } export default App;
hooks
import React, { Component, useState } from 'react'; const App = () => { const [count, setCount] = useState(0); return ( <> {count} setCount(count + 1)}>증가 ); }; export default App;
useState가 바로 Hook으로 Hook을 호출해 함수 컴포넌트(function component)안에 state를 추가했습니다.
useState는 현재의 state 값과 이 값을 업데이트하는 함수를 쌍으로 제공합니다. 이 함수를 핸들러나 다른 곳에서 호출할 수 있습니다.
useState는 클래스에서 사용하는 this.setState와 유사하지만 이전 state와 새로운 state를 합치지 않는다는 차이점이 존재 합니다.
jsx 표현식을 사용하여 onClick이벤트 내부에 화살표 함수를 작성하여 바로 처리할 수도 있지만 아래와 같이 함수형으로 만들수도 있습니다.
import React, { useState } from "react"; export default function App() { const [item, setItem] = useState(0); const incrementItem = () => setItem(item + 1); const decrementItem = () => setItem(item - 1); return ( number : {item} 증가감소 ); }
useState의 인자로 무엇을 넘겨주어야 하나?
위의 예시에서 useState인자로 0을 전달한 이유는 카운터 증가를 시작할 때 0에서부터 시작하기 위해 state인 count의 초기 값을 0으로 설정한 것입니다. 만약 1부터 시작하고 싶으시다면 useState(1)로 인자 1을 전달하면 됩니다.
useState는 무엇을 반환?
state 변수, 해당 변수를 갱신할 함수 이 두 가지를 반환합니다. 가장 쉽게 클래스컴포넌트와 비교 해보도록 하겠습니다.
useState은 클래스 컴포넌트의 this.state.count와 this.setState와 유사합니다.
만일 아래와 같이 hook을 호출해 state를 추가하고 count를 0으로 초기화 했다면
const App = () => { const [count,setCount] = useState(0) }
클래스 컴포넌트에서는 아래와 같음
class App extends Component { state = { count: null, }; setCount() { this.setState({ count: 0, }); }
useMemo()
useMemo는 컴포넌트의 성능을 최적화시킬 수 있는 대표적인 react hooks 중 하나입니다. useMemo에서 Memo는 Memoization을 뜻합니다. memoization이란 기존에 수행한 연산의 결괏값을 어딘가에 저장해 두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법을 말합니다.
먼저 시작하기 전에 함수형 컴포넌트에 대해 꼭 아셔야 합니다. 아래 예제를 보면서 같이 살펴보겠습니다. 함수형 컴포넌트는 렌더링
Component 함수 호출 모든 내부 변수 초기화의 순서를 거칩니다. Component가 렌더링이 될 때마다 value라는 변수가 초기화됩니다. 따라서 calculate 함수는 반복적으로 호출됩니다. calculate 함수가 무거운 일을 하는 함수라면 굉장히 비효율적이겠죠? 무거운 일을 반복적으로 하니깐요.
function Component() { const value = calculate(); return {value} } function calculate() { return 10; }
useMemo는 첫 번째 인자로 콜백 함수를, 두 번째 인자로 의존성 배열을 받습니다. 두 번째 인자인 배열의 요소 값이 업데이트될 때만 콜백 함수를 다시 호출해서 memoization 된 값을 업데이트 해줘서 다시 memoization을 해줍니다. 만약에 빈 배열([])을 넘겨주면 맨 처음 컴포넌트가 마운트 되었을 때만 값을 계산하고 이후에는 항상 memoization된 값을 꺼내 와서 사용합니다.
// 첫 번째 인자 콜백함수 // 두 번째 인자 의존성배열 const value = useMemo(() => { return calculate(); },[item])
[예제]
object는 객체 타입이어서 일반 원시 타입과는 다르게 값이 저장될 때 주소 값으로 저장이 됩니다. 즉, 메모리 상의 주소가 다르게 저장되어 있는 것입니다. const location = { country: isKorea ? '한국' : '일본' }; 눈으로 보이기에는 똑같지만 저장된 메모리 상의 주소가 완전히 다르기 때문에 useEffect의 location은 변경이 되었다고 생각할 수 있습니다.
const location = { country: isKorea ? '한국' : '일본' }; useEffect(() => { console.log('useEffect... 호출'); }, [location])
따라서 이것을 해결해주려면 useMemo를 아래와 같이 사용해서 memoization 해주시면 됩니다.
import { useMemo, useEffect, useState } from 'react'; function App() { const [number, setNumber] = useState(0); const [isKorea, setIsKorea] = useState(true); // const location = { country: isKorea ? '한국' : '일본' }; const location = useMemo(() => { return { country: isKorea ? '한국' : '일본' } }, [isKorea]) useEffect(() => { console.log('useEffect... 호출'); // 뭔가 오래 걸리는 작업 }, [location]) return ( 하루에 몇 끼 먹어요? setNumber(e.target.value)}/> 어느 나라에 있어요? 나라: {location.country} setIsKorea(!isKorea)}>Update ); } export default App;
출처: https://itprogramming119.tistory.com/entry/React-useMemo-사용법-및-예제 [코딩병원:티스토리]
useCallback()
useMemo 는 특정 결과값을 재사용 할 때 사용하는 반면, useCallback 은 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용합니다.
useCallback 사용법
const memoizedCallback = useCallback(function, deps);
useCallback은 첫 번째 인자로 넘어온 함수를, 두 번째 인자로 넘어온 배열 형태의 함수 실행 조건의 값이 변경될 때까지 저장해놓고 재사용할 수 있게 해 줍니다.
예를 들어 리액트 컴포넌트 안에 함수가 선언되어있을 때 이 함수는 해당 컴포넌트가 렌더링 될 때마다 새로운 함수가 생성되는데, useCallback을 사용하면 해당 컴포넌트가 렌더링 되더라도 그 함수가 의존하는 값(deps)들이 바뀌지 않는 한 기존 함수를 재사용할 수 있습니다.
const add = () => x + y;
예시로 위와 같은 함수가 있다고 할 때 useCallback을 사용하면 아래와 같이 적용할 수 있습니다.
const add = useCallback(() => x + y, [x, y]);
사용예
React.memo와 함께 사용하기
useCallback() hook 함수는 자식 컴포넌트의 랜더링의 불필요한 랜더링을 줄이기 위해서 React.memo() 함수와도 사용할 수 있습니다.
예를 들어, 방이름(room), 조명 켜짐 여부(on), 조명 제어 함수(toggle)를 prop으로 Light 컴포넌트를 작성해보겠습니다.
import React from "react"; function Light({ room, on, toggle }) { console.log({ room, on }); return ( {room} {on ? "????" : "⬛"} ); }
그리고 React.memo() 함수로 이 컴포넌트를 감싸줍니다. 이렇게 React 컴포넌트 함수를 React.memo() 함수로 감싸주면 해당 컴포넌트 함수는 props 값이 변경되지 않는 한 다시 호출되지 않습니다.
Light = React.memo(Light);
다음으로 3개의 방의 스위치를 중앙 제어해주는 SmartHome 컴포넌트를 작성합니다.
import React, { useState, useCallback } from "react"; function SmartHome() { const [masterOn, setMasterOn] = useState(false); const [kitchenOn, setKitchenOn] = useState(false); const [bathOn, setBathOn] = useState(false); const toggleMaster = () => setMasterOn(!masterOn); const toggleKitchen = () => setKitchenOn(!kitchenOn); const toggleBath = () => setBathOn(!bathOn); return ( <> ); }
이 컴포넌트를 이용해서 침실의 조명을 켜보면 침실 뿐만 아니라 다른 모든 방에 대한 Light 컴포넌트 함수가 호출이 되는 것이 콘솔 로그로 확인될 것입니다.
{room: "침실", on: true} {room: "주방", on: false} {room: "욕실", on: false}
조명을 키거나 끄는 방에 대한 Light 컴포넌트 함수만 호출되게 하고 싶어서, React.memo()를 사용한 것인데 무엇이 문제일까요? 바로 조명을 제어할 때 쓰이는 toggleMaster(), toggleKitchen(), toggleBath() 함수의 참조값이 SmartHome 컴포넌트가 랜더링될 때마다 모두 바뀌어버리기 때문입니다.
이 문제를 해결하려면 모든 조명 제어 함수를 useCallback() hook 함수로 감싸고 두 번째 인자로 각 함수가 의존하고 있는 상태를 배열로 넘겨야 합니다.
useRef()
Ref를 사용해야 할 때
Ref의 바람직한 사용 사례는 다음과 같습니다.
- 포커스, 텍스트 선택영역, 혹은 미디어의 재생을 관리할 때.
- 애니메이션을 직접적으로 실행시킬 때.
- 서드 파티 DOM 라이브러리를 React와 같이 사용할 때.
선언적으로 해결될 수 있는 문제에서는 ref 사용을 지양하세요.
예를 들어, Dialog 컴포넌트에서 open()과 close() 메서드를 두는 대신, isOpen이라는 prop을 넘겨주세요.
. 컴포넌트에 focus를 위치시킬 필요가 있는 경우.
- 예를 들어, 값을 여러개 일력하고 첫 번째 칸으로 이동해야 하는 경우 필요하다.
//InputTest.js import React, { useState, useRef } from 'react'; function InputTest() { const [text, setText] = useState(''); const nameInput = useRef(); const onChange = e => { setText(e.target.value) }; const onReset = () => { setText(''); nameInput.current.focus(); }; return (초기화 내용: {text} ); } export default InputTest;
https://yoonjong-park.tistory.com/entry/React-useRef-는-언제-사용하는가
useRef 함수는 current 속성을 가지고 있는 객체를 반환하는데, 인자로 넘어온 초기값을 current 속성에 할당합니다. 이 current 속성은 값을 변경해도 상태를 변경할 때 처럼 React 컴포넌트가 다시 랜더링되지 않습니다. React 컴포넌트가 다시 랜더링될 때도 마찬가지로 이 current 속성의 값이 유실되지 않습니다.
useRef 훅 함수가 반환하는 객체의 이러한 독특한 성질을 이용하여 startCounter()와 stopCounter() 함수를 구현해보았습니다.
import React, { useState, useRef } from "react"; function ManualCounter() { const [count, setCount] = useState(0); const intervalId = useRef(null); console.log(`랜더링... count: ${count}`); const startCounter = () => { intervalId.current = setInterval( () => setCount((count) => count + 1), 1000 ); console.log(`시작... intervalId: ${intervalId.current}`); }; const stopCounter = () => { clearInterval(intervalId.current); console.log(`정지... intervalId: ${intervalId.current}`); }; return ( <> 자동 카운트: {count} 시작정지 ); }
Hook의 규칙
1. Hook 은 무조건 최상위 레벨에서만 호출해야 한다.
2.리액트 함수 컴포넌트에서만 Hook 을 호출해야 한다.
Hook 규칙을 잡아주는 플러그인
=> eslint-plugin-react-hooks
실습
커스텀 훅 useCounter.jsx 생성
import React, { useState } from 'react'; const useCounter = (initalValue) => { const [count, setCount] = useState(initalValue); const increaseCount = () => setCount((count) => count + 1); const decreaseCount = () => setCount((count) => Math.max(count - 1, 0)); return [count, increaseCount, decreaseCount]; }; export default useCounter;
Accommodate.jsx
import React, { useEffect, useState } from 'react'; import useCounter from './useCounter'; const MAX_CAPACITY =10; const Accommodate = (props) => { const [isFull, setIsFull] = useState(false); const [count, increaseCount, decreaseCount] = useCounter(0); useEffect(() => { console.log("================="); console.log("useEffect() is called"); console.log(`isFull : ${isFull}`); }); useEffect(() => { setIsFull(count >= MAX_CAPACITY); console.log(`Current count value: ${count}`); }, [count]); return ( <div style={{ padding: 16 }}> <p>{`총 ${count} 명 수용했습니다.` }</p> <button onClick={increaseCount} disabled={isFull}>입장</button> <button onClick={decreaseCount} >퇴장</button> {isFull && <p style={{color:"red"}}>정원이 가득찼습니다.</p>} </div> ); }; export default Accommodate;
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import Library from './chapter_03/Library'; import Clock from './chapter_04/Clock'; import CommentList from './chapter_05/CommentList'; import NotificationList from './chapter_06/NotificationList'; import Accommodate from './chapter_07/Accommodate'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> {/* <App /> */} {/* <Library /> */} {/* <Clock /> */} {/* <CommentList /> */} {/* <NotificationList /> */} <Accommodate /> </React.StrictMode> ); reportWebVitals();
소스: https://github.com/braverokmc79/React-firstTime
댓글 ( 4)
댓글 남기기