별코딩
유튜브 강의 목록 : https://www.youtube.com/playlist?list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO
1.useState 15분만에 마스터하기
2.useEffect 깔끔하게 마스터하기
3.useRef 완벽 정리 1# 변수 관리
4.useRef 완벽 정리 2# DOM 요소 접근
5.useContext + Context API
6.useMemo 제대로 사용하기
7. useCallback 짱 쉬운 강의
8. useReducer 확실히 정리해드려요
9.React.memo로 컴포넌트 최적화하기 (ft. useMemo, useCallback)
10.Custom Hooks 커스텀 훅
* 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-next-app nextjs-tutorial
소스 : https://github.com/braverokmc79/react-hooks-drunk
1.useState 15분만에 마스터하기
1) Stu1And1
import React, { useState } from 'react' function Stu1And1() { const [time, setTime] = useState(1); const handleClick = () => { let newTime; if (time >= 12) { newTime = 1; } else { newTime = time + 1; } setTime(newTime); } return ( <div> <span>현재 시각 : {time}시</span> <button onClick={handleClick}>Update</button> </div> ) } export default Stu1And1
2) Stu1And2
import React, { useState } from 'react' const heavyWork = () => { console.log("엄청 무거운 작업 초기값 콜백 설정시 맨 처음 랜더링 될때만 실행 !!!"); return ['홍길동', '김민수']; } function Stu1And2() { const [Names, setNames] = useState(() => { return heavyWork(); }); const [Input, setInput] = useState(''); const handleInputChange = (e) => { setInput(e.target.value); } const handleUpload = (e) => { setNames((prevState) => { console.log("이전 state : ", prevState); return [Input, ...prevState]; }) // setNames([...Names, Input]); } return ( <div> <input type="text" value={Input} onChange={handleInputChange} /> <button onClick={handleUpload}>Upload</button> <br /> { Names.map((name, idx) => { return <p key={idx}>{name}</p> }) } </div> ) } export default Stu1And2
2.useEffect 깔끔하게 마스터하기
Stu2And1
import { useEffect, useState } from "react" function Stu2And1() { const [Count, setCount] = useState(1); const [Name, setName] = useState(''); const handleCountUpdate = () => { setCount(Count + 1); } const handleInputChange = (e) => { setName(e.target.value); } //랜더링 될때마다 매번 실행됨 useEffect(() => { console.log('랜더링 될때마다 매번 실행됨'); },) //마운팅 + count 변화 될때마다 매번 실행됨 useEffect(() => { console.log('count 변화'); }, [Count]) //마운팅 + Name 변화 될때마다 매번 실행됨 useEffect(() => { console.log('Name 변화'); }, [Name]) useEffect(() => { console.log('마운팅 될 1번만 .....'); }, []) return ( <div> <button onClick={handleCountUpdate}>Update</button> <span>count : {Count}</span> <br /><br /> <input type="text" value={Name} onChange={handleInputChange} /> <span>{Name}</span> </div> ) } export default Stu2And1
Stu2And2
useEffect 에서 return 함수는은 컴포넌트가 unmount 될때 종료 처리해 준다
import React from 'react' import { useEffect } from 'react'; function Stu2And2() { useEffect(() => { const timer = setInterval(() => { console.log("타이머 돌아가는 중...."); }, 1000); //다음 return 은 timer 컴포넌트가 unmount 될때 종료 처리해 준다 return () => { clearInterval(timer); console.log("타이머가 종료되었습니다."); } }, []) return ( <div> <span>타이머를 시작합니다. 콘솔을 보세요.!</span> </div> ) } export default Stu2And2
3.useRef 완벽 정리 1# 변수 관리
ref DOM에 직접 접근하기
DOM요소에 이름을 달아 직접 접근할 때
1)Stu3And1
import React, { useRef } from 'react' import { useState } from 'react'; function Stu3And1() { const [Count, setCount] = useState(0); const countRef = useRef(0); console.log("랜더링 ...."); const increaseCountState = () => { setCount(Count + 1); } const increaseCountRef = () => { countRef.current = countRef.current + 1; console.log("Ref :", countRef.current); } return ( <div> <p>State : {Count}</p> <p>Ref : {countRef.current}</p> <button onClick={increaseCountState}>State 올려</button> <button onClick={increaseCountRef}>Ref 올려</button> </div> ) } export default Stu3And1
2)Stu3And2
ref 값을 유지 한다.
import React from 'react' import { useState, useRef } from 'react'; function Stu3And2() { const [Renderer, setRenderer] = useState(0); const countRef = useRef(0); let countVar = 0; const doRendering = () => { setRenderer(Renderer + 1); } const increaseRef = () => { countRef.current = countRef.current + 1; console.log('ref : ', countRef.current); } const increaseVar = () => { countVar = countVar + 1; console.log('var : ', countVar); } const printResults = () => { console.log(`ref : ${countRef.current} ,var :${countVar}`); } return ( <div> <p>Ref : {countRef.current}</p> <p>Var : {countVar}</p> <button onClick={doRendering}>랜더!</button> <button onClick={increaseRef}>Ref 올려</button> <button onClick={increaseVar}>Var 올려</button> <button onClick={printResults}>Ref Var 값 출력</button> </div> ) } export default Stu3And2
3)Stu3And3
import React from 'react' import { useState, useRef, useEffect } from 'react'; function Stu3And3() { const [Count, setCount] = useState(1); const renderCount = useRef(1); useEffect(() => { renderCount.current = renderCount.current + 1; console.log("랜더링 수 : ", renderCount.current); }) return ( <div> <p>Count: {Count}</p> <button onClick={() => setCount(Count + 1)}>올려</button> </div> ) } export default Stu3And3
4.useRef 완벽 정리 2# DOM 요소 접근
Stu4And1
import React, { useEffect, useRef } from 'react' function Stu4And1() { const inputRef = useRef(""); useEffect(() => { inputRef.current.focus(); console.log(inputRef.current); }, []) const login = () => { alert(`환영합니다. ${inputRef.current.value}!`); inputRef.current.focus(); } return ( <div> <input type="text" placeholder='username' ref={inputRef} /> <button onClick={login}>로그인</button> </div > ) } export default Stu4And1
5.useContext + Context API
Redux vs React context api 어떤 경우에 사용 할 까?
Redux는 리액트 에서 많이 사용되는 state 관리 라이브러리 이고 context api는 리액트에서 기본으로 제공하는 state 관리 api 이다. 대부분에 경우 state management 라이브러리인 redux를 사용하는데 왜 그런지 알아보도록 하자.
먼저 Redux와 context api에 특징에 대해서 알아보자
Redux
컴포넌트에 state를 prop으로 넘겨 줄 때 어느 컴포넌트 깊이와 상관 없이 넘겨줄 수 있다.
만약에 컴포넌트 구조가 아래와 같이 되어 있는데
/<Parent> <Child1> <Child2> <Child3></Child3> </Child2> </Child1> </Parent> const child3Reducer = { candies: [] iceCreams: [] } const Child3 = () => { const iceCreams = useSelector(state => state.child3Reducer.iceCreams) return null }
만약에 redux가 없다면 parent에서 state로 child3Data를 관리하고 Child1 Child2 Child3에게 prop으로 넘겨줘야 합니다. prop이 1-2개면 괜찮은데 prop이 많아지면 머리가 아파지고 또한 불필요한 rendering이 일어날 가능성이 크기 때문에 비효율 적일 수 있습니다. 하지만 코드에 보는 것 처럼 useSelector나 connect로 reducer에서 데이터를 바로 가져와서 사용할 수 있습니다.
연결된 state가 바뀌었을 때만 컴포넌트를 다시 랜더링 한다.
위에 보이는것 처럼 child3Reducer에서 iceCreams state만 사용할 때 iceCreams가 바뀌었을 때만 다시 랜더링을 시킨다.
랜더링을 할지 안할지 코드를 통해서 컨트롤 할 수 있다.
함수형 컴포넌트를 사용할 때 memo로 컴포넌트를 감싸주면 불필요한 랜더링을 피할 수 있다. reselect 같은 라이브러리를 사용 할 수 도 있다.
Context api
- 리덕스와 비슷하게 필요한 컴포넌트에서 바로 useContext를 사용해서 사용할 수 있다.
- 코드를 통해서 랜더링을 할지 않할지 선택이 불가능 하다
- 더 많은 코드를 써야된다.
- 완전히 간단한 앱에서만 사용하는 것을 추천한다.
* 사용방법
1. 다음과 같이 context.js 파일을 생성한다.
ThemeContext.js
import { createContext } from 'react' export const ThemeContext = createContext(null);
2. <ThemeContext.Provider 형태로 감싸주고 value 값에 변수 설정 한다.
<ThemeContext.Provider value={{ isDark, setIsDark }}>
import React from 'react' import { useState } from 'react'; import Page from './Page'; import "../../../App.css" import "./Section5.css"; import { ThemeContext } from './context/ThemeContext'; import { UserContext } from './context/UserContext'; function Stu5And1() { const [isDark, setIsDark] = useState(false); return ( // <Page isDark={isDark} setIsDark={setIsDark} /> <UserContext.Provider value={'사용자'} > <ThemeContext.Provider value={{ isDark, setIsDark }}> <Page /> </ThemeContext.Provider> </UserContext.Provider> ) } export default Stu5And1
3. useContext 로 호출 한다.
const { isDark, setIsDark } = useContext(ThemeContext);
Footer
import React, { useContext } from 'react' import { ThemeContext } from './context/ThemeContext'; function Footer(porps) { const { isDark, setIsDark } = useContext(ThemeContext); const toggleTheme = () => { setIsDark(!isDark); } return ( <footer className='footer' style={{ backgroundColor: isDark ? 'black' : 'lightgray' }}> <button className='button' onClick={toggleTheme}>Dark Mode</button> </footer> ) } export default Footer
4. 기타 참조
Page.js
import React, { useContext } from 'react' import Header from './Header'; import Content from './Content'; import Footer from './Footer'; import { ThemeContext } from './context/ThemeContext'; function Page({ isDark, setIsDark }) { const data = useContext(ThemeContext); console.log("data1 ", data); return ( // <div className='page'> // <Header isDark={isDark} /> // <Content isDark={isDark} /> // <Footer isDark={isDark} setIsDark={setIsDark} /> // </div> <div className='page'> <Header /> <Content /> <Footer /> </div> ) } export default Page
Header.js
import React, { useContext } from 'react' import { ThemeContext } from './context/ThemeContext'; import { UserContext } from './context/UserContext'; function Header() { const { isDark } = useContext(ThemeContext); const user = useContext(UserContext); return ( <header className='header' style={{ backgroundColor: isDark ? 'black' : 'lightgray', color: isDark ? 'white' : 'black' }}> <h1>Welcome {user}!</h1> </header> ) } export default Header
Content.js
import React, { useContext } from 'react' import { ThemeContext } from './context/ThemeContext'; function Content() { const { isDark } = useContext(ThemeContext); return ( <div className='header' style={{ backgroundColor: isDark ? 'black' : 'white', color: isDark ? 'white' : 'black' }}> <p>홍길동님,좋은 하루 되세요.</p> </div> ) } export default Content
UserContext.js
import { createContext } from 'react'; export const UserContext = createContext(null);
6.useMemo 제대로 사용하기
userMemo 를 사용 안할 경우 easyCalculate 호출할때도 hardCalculate 실행 된다.
import { useState } from 'react'; const hardCalculate = (number) => { console.log("어려운 계산!"); for (let i = 0; i < 9999999; i++) { } //생각하는 시간 return number + 10000; } const easyCalculate = (number) => { console.log("쉬운 계산!"); return number + 1; } function Stu6And1() { const [hardNumber, setHardNumber] = useState(1); const [easyNumber, setEasyNumber] = useState(1); const hardSum = hardCalculate(hardNumber); const easySum = easyCalculate(easyNumber); return ( <div> <h3>어려운 계산기</h3> <input type="number" value={hardNumber} onChange={(e) => setHardNumber(Number(e.target.value))} /> <span>+10000 ={hardSum}</span> <br /><br /> <h3>쉬운 계산기</h3> <input type="number" value={easyNumber} onChange={(e) => setEasyNumber(Number(e.target.value))} /> <span>+10000 ={easySum}</span> </div> ) } export default Stu6And1
====>
import { useMemo, useState } from 'react'; const hardCalculate = (number) => { console.log("어려운 계산!"); for (let i = 0; i < 9999999; i++) { } //생각하는 시간 return number + 10000; } const easyCalculate = (number) => { console.log("쉬운 계산!"); return number + 1; } function Stu6And1() { const [hardNumber, setHardNumber] = useState(1); const [easyNumber, setEasyNumber] = useState(1); //const hardSum = hardCalculate(hardNumber); const hardSum = useMemo(() => { return hardCalculate(hardNumber); }, [hardNumber]); const easySum = easyCalculate(easyNumber); return ( <div> <h3>어려운 계산기</h3> <input type="number" value={hardNumber} onChange={(e) => setHardNumber(Number(e.target.value))} /> <span>+10000 ={hardSum}</span> <br /><br /> <h3>쉬운 계산기</h3> <input type="number" value={easyNumber} onChange={(e) => setEasyNumber(Number(e.target.value))} /> <span>+10000 ={easySum}</span> </div> ) } export default Stu6And1
★객체로 useEffect 적용시에 userMemo 사용 할것.
import { useEffect, useState, useMemo } from 'react'; function Stu6And2() { const [number, setNumer] = useState(0); const [isKorea, setIsKorea] = useState(true); // const location = isKorea ? '한국' : '외국'; // const location = { // country: isKorea ? '한국' : '외국' // } const location = useMemo(() => { return { country: isKorea ? '한국' : '외국' } }, [isKorea]); useEffect(() => { console.log("useEffect 호출"); }, [location]); return ( <div> <h2>하루에 몇끼 먹어요?</h2> <input type="number" value={number} onChange={(e) => setNumer(e.target.value)} /> <hr /> <h2>어느 나라에 있어요?</h2> {/* <p>나라: {location}</p> */} <p>나라: {location.country}</p> <button onClick={() => setIsKorea(!isKorea)}>비행기 타자</button> </div> ) } export default Stu6And2
7. useCallback 짱 쉬운 강의
useCallback
useCallback은 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용.
useCallback은 React.memo와 함께 자식 컴포넌트의 불필요한 렌더링을 최적화할 수 있다.
왜 useCallback을 사용해야 할까요?
현재 하위 컴포넌트에 전달하는 콜백 함수가 inline 함수로 사용되거나, 컴포넌트 내에서 함수를 생성하고 있다면 새로운 함수가 만들어지게 됩니다. 예를 들어보자면, number 안에 someFunction 함수들은 컴포넌트가 리렌더링 될 때 마다 새로 만들어지게 됩니다.
함수를 재 선언 하는것은 CPU, 큰 메모리도 차지하지 않지만, 한번 만든 함수를 재 사용하고, 필요할 때만 재 생성 하는것은 중요합니다.
예1)
import React, { useCallback } from 'react' import { useState, useEffect } from 'react'; function Stu7And1() { const [number, setNumber] = useState(0); const [toggle, setToggle] = useState(false); //1.useCallback 을 사용하지 않고 다음과 같은 함수 사용시에 toggle 버튼 클릭시 // someFunction 함수도 호출된다. // const someFunction = () => { // console.log(`someFunc : number : ${number}`); // return; // }; const someFunction = useCallback(() => { console.log(`someFunc : number : ${number}`); return; }, [number]); useEffect(() => { console.log('someFunction 이 변경되었습니다.'); }, [someFunction]) return ( <div> <input type="number" value={number} onChange={(e) => setNumber(e.target.value)} /> <button onClick={() => setToggle(!toggle)}>{toggle.toString()}</button> <br /> <button onClick={someFunction}>Call SomeFunce</button> </div> ) } export default Stu7And1
예2)
Stu7And2.js
import { useState, useCallback } from 'react'; import Box from './Box'; function Stu7And2() { const [size, setSize] = useState(100); const [isDark, setIsDark] = useState(false); // const createBoxStyle = () => { // return { // backgroundColor: 'pink', // width: `${size}px`, // height: `${size}px` // } // } const createBoxStyle = useCallback(() => { return { backgroundColor: 'pink', width: `${size}px`, height: `${size}px` } }, [size]); return ( <div style={{ background: isDark ? "black" : "white" }}> <input type="number" value={size} onChange={(e) => setSize(e.target.value)} /> <button onClick={() => setIsDark(!isDark)}>Change Theme</button> <Box createBoxStyle={createBoxStyle} /> </div> ) } export default Stu7And2
Box.js
import { useEffect, useState } from 'react'; function Box({ createBoxStyle }) { const [style, setStyle] = useState({}); useEffect(() => { console.log("박스 키우기11"); setStyle(createBoxStyle()); }, [createBoxStyle]); return ( <div style={style}></div> ) } export default Box
8. useReducer 확실히 정리해드려요
예1)
Stu8And1.js
import React, { useReducer, useState } from 'react' //reducer - state를 업데이트 하는 역할 (은행) //dispatch - state 업데이트를 위한 요구 //action -요구의 내용 const ACTION_TYPES = { deposit: 'deposit', withdraw: 'withdraw' } const reducer = (state, action) => { console.log('reducer 가 일을 합니다.!', state, action); switch (action.type) { case ACTION_TYPES.deposit: return state + action.payload; case ACTION_TYPES.withdraw: return state - action.payload; default: return state; } }; function Stu8And1() { const [number, setNumber] = useState(0); const [money, dispatch] = useReducer(reducer, 0); return ( <div> <h2>useRducer 은행에 오신것을 환영합니다.</h2> <p>잔고: {money}원</p> <input type="number" value={number} onChange={(e) => setNumber(parseInt(e.target.value))} step="1000" /> <button onClick={() => { dispatch({ type: 'deposit', payload: number }); }}>예금</button> <button onClick={() => { dispatch({ type: 'withdraw', payload: number }); }} >출금</button> </div> ) } export default Stu8And1
예2)
Student.js
import React from 'react' function Student({ name, dispatch, id, isHere }) { return ( <div> <br /> <p> <span style={{ textDecoration: isHere ? 'line-through' : 'none', color: isHere ? 'gray' : 'black' }} onClick={() => dispatch({ type: 'mark-student', payload: { id } })} > {name} </span> <button onClick={() => { dispatch({ type: 'delete-student', payload: { id } }) }}>삭제</button> </p> </div> ) } export default Student
Stu8And2.js
import React from 'react' import { useState, useReducer } from 'react'; import Student from './Student'; const ACTION_TYPES = { ADD_STUDENT: 'add-student', DELETE_STUDENT: 'delete-student', MARK_STUDENT: 'mark-student' } const reducer = (state, action) => { switch (action.type) { case ACTION_TYPES.ADD_STUDENT: console.log("학생 추가: ", state, action); const newStudent = { id: Date.now(), name: action.payload.name, isHere: false } return { count: state.count + 1, students: [...state.students, newStudent] }; case ACTION_TYPES.DELETE_STUDENT: console.log("학생 삭제: ", state, action); return { count: state.count - 1, students: state.students.filter((student) => student.id !== action.payload.id) } case ACTION_TYPES.MARK_STUDENT : return { count: state.count, students: state.students.map((student) => { if (student.id === action.payload.id) { return { ...student, isHere: !student.isHere } } return student; }) } default: return state; } }; const initialState = { count: 0, students: [] } function Stu8And2() { const [name, setName] = useState(''); const [studentInfo, dispatch] = useReducer(reducer, initialState); return ( <div> <h1>출석부</h1> <p>총 학생 수 : {studentInfo.count}</p> <input type="text" placeholder='이름을 입력해주세요.' value={name} onChange={(e) => setName(e.target.value)} /> <button onClick={() => dispatch({ type: 'add-student', payload: { name } })}>추가</button> {studentInfo.students.map(student => { return (<Student key={student.id} name={student.name} dispatch={dispatch} id={student.id} isHere={student.isHere} />) })} </div> ) } export default Stu8And2;
9.React.memo로 컴포넌트 최적화하기 (ft. useMemo, useCallback)
예1)
Stu9And1
import React, { useState } from 'react' import Child from './Child'; function Stu9And1() { const [parentAge, setParentAge] = useState(0); const [childAge, setChildAge] = useState(0); const incrementParentAge = () => { setParentAge(parentAge + 1); } const incrementChildAge = () => { setChildAge(childAge + 1); } console.log('부모 컴포넌트가 랜더링 되었어요.'); return ( <div> <h1>부모</h1> <p>age : {parentAge}</p> <button onClick={incrementParentAge} >부모 나이 증가</button> <button onClick={incrementChildAge} >자식 나이 증가</button> <Child name={'홍길동'} age={childAge} /> </div> ) } export default Stu9And1
memo 사용법은 export 시에 memo(Child) 로 감싸주기만 하면된다.
Child .js
import React, { memo } from 'react' function Child({ name, age }) { console.log('자녀도 랜더링 되었어요.'); return ( <div style={{ border: '2px solid powderblue', padding: '10px' }}> <h3>자녀</h3> <p>name:{name}</p> <p>age : {age}살</p> </div> ) } export default memo(Child);
예2)memo, useMemo 과 함께 사용
Stu9And2.js
import React, { useMemo, useState } from 'react' import Child2 from './Child2'; function Stu9And2() { const [parentAge, setParentAge] = useState(0); const incrementParentAge = () => { setParentAge(parentAge + 1); } console.log('부모 컴포넌트가 랜더링 되었어요.'); const name = useMemo(() => { return { lastName: "홍", firstName: "길동" } }, []); return ( <div> <h1>부모</h1> <p>age : {parentAge}</p> <button onClick={incrementParentAge} >부모 나이 증가</button> <Child2 name={name} /> </div> ) } export default Stu9And2
Child2.js
import React, { memo } from 'react' function Child2({ name }) { console.log('자녀도 랜더링 되었어요.'); return ( <div style={{ border: '2px solid powderblue', padding: '10px' }}> <h3>자녀</h3> <p>성:{name.lastName}</p> <p>이름:{name.firstName}</p> </div> ) } export default memo(Child2);
예3)memo, useCallback 과 함께 사용
Stu9And3
import React, { useState, useCallback } from 'react' import Child3 from './Child3'; function Stu9And3() { const [parentAge, setParentAge] = useState(0); const incrementParentAge = () => { setParentAge(parentAge + 1); } console.log('부모 컴포넌트가 랜더링 되었어요.'); const tellMe = useCallback(() => { console.log('길동아 사랑해 '); }, []); return ( <div> <h1>부모</h1> <p>age : {parentAge}</p> <button onClick={incrementParentAge} >부모 나이 증가</button> <Child3 name={'홍길동'} tellMe={tellMe} /> </div> ) } export default Stu9And3
Child3
import React, { memo } from 'react' function Child3({ name, tellMe }) { console.log('자녀도 랜더링 되었어요.'); return ( <div style={{ border: '2px solid powderblue', padding: '10px' }}> <h3>자녀</h3> <p>이름:{name}</p> <button onClick={tellMe}>엄마 나 사랑해?</button> </div> ) } export default memo(Child3);
10.Custom Hooks 커스텀 훅
예1)
import { useInput } from './useInput'; function Stu10And1() { const [inputValue, handleChange] = useInput("안녕"); const [inputValue2, handleChange2] = useInput("123"); const handleSubmit = () => { alert(inputValue); } return ( <div> <h1>useInput</h1> <input value={inputValue} onChange={handleChange} /> <input value={inputValue2} onChange={handleChange2} /> <button onClick={handleSubmit}>확인</button> </div> ) } export default Stu10And1
custom hooks input
import { useState } from 'react'; export function useInput(initialValue) { const [inputValue, setInputValue] = useState(initialValue); const handleChange = (e) => { setInputValue(e.target.value); }; return [inputValue, handleChange]; }
예2)
Stu10And1
import { useInput } from './useInput'; function displyMessage(message) { alert(message); } function Stu10And1() { const [inputValue, handleChange, handleSubmit] = useInput("안녕", displyMessage); return ( <div> <h1>useInput</h1> <input value={inputValue} onChange={handleChange} /> <button onClick={handleSubmit}>확인</button> </div> ) } export default Stu10And1
custom hooks useInput
import { useState } from 'react'; export function useInput(initialValue, submitAction) { const [inputValue, setInputValue] = useState(initialValue); const handleChange = (e) => { setInputValue(e.target.value); }; const handleSubmit = () => { setInputValue(""); submitAction(inputValue); } return [inputValue, handleChange, handleSubmit]; }
예3)
Stu10And2
import { useFetch } from './useFetch'; let baseUrl = "https://jsonplaceholder.typicode.com"; function Stu10And2() { const { data, fetchUrl } = useFetch(baseUrl, "users"); const { data: userData } = useFetch(baseUrl, "users"); const { data: postData } = useFetch(baseUrl, "posts"); return ( <div> <h1>useFetch</h1> <button onClick={() => fetchUrl('users')}>Users</button> <button onClick={() => fetchUrl('posts')}>Posts</button> <button onClick={() => fetchUrl('todos')}>Todos</button> <br /> <textarea style={{ width: 300, height: 300 }} value={data && JSON.stringify(data, null, 2)}> </textarea> <br /><br /> <h1>User</h1> {userData && <pre> {JSON.stringify(userData[0], null, 2)}</pre>} <br /><br /> <h1>Post</h1> {postData && <pre> {JSON.stringify(postData[0], null, 2)}</pre>} </div> ) } export default Stu10And2;
custom hooks useFetch
import { useState, useEffect } from 'react'; export function useFetch(baseUrl, initialType) { const [data, setData] = useState(null); const fetchUrl = (type) => { fetch(baseUrl + "/" + type) .then((res) => res.json()) .then((res) => setData(res)); } useEffect(() => { fetchUrl(initialType); }, []); return { data, fetchUrl }; }
댓글 ( 4)
댓글 남기기