
๐ Vue์ React๋ ํฐ ์ฐจ์ด์ ์ด ์๋๋ฐ, ๊ทธ ์ค ํ๊ฐ์ง๋ ์๋ฐฉํฅ๊ณผ ๋จ๋ฐฉํฅ์ด๋ค.
๐ Vue๊ฐ์ ๊ฒฝ์ฐ์๋ Inputํ๊ทธ๋ฅผ ์ฌ์ฉํ ๋, v-model์ ์ฌ์ฉํ์ฌ input์ ์์ฑ๋๋ ๋ด์ฉ์ด ์๋ฐฉํฅ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์์ฑ์ด ๋์ง๋ง, React์ธ ๊ฒฝ์ฐ์๋ ๊ทธ๋ ์ง์๊ธฐ ๋๋ฌธ์ Input์ ์ฌ์ฉํ๊ธฐ ์ํด์ ์ด๋ฒคํธ๋ฅผ ๊ฑธ์ด ์๋ฐฉํฅ์ ํํ๋ก ์ฌ์ฉํ๊ฒ๋๋ค.
import {ITodo} from "../../type/todo.ts";
import {ChangeEvent, useState} from "react";
const initState: ITodo = {
title : '',
writer : '',
dueDate : '',
}
function TodoInput() {
const [todo, setTodo] = useState<ITodo>(initState) // ๊ธฐ๋ณธ๊ฐ์ initalState
const handleChange = (e:ChangeEvent<HTMLInputElement>) => { // ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๊ฐ์ ๋ํ ์ด๋ฒคํธ
console.log(e)
console.log(e.target)
//console.log(e.target.value);
//console.log(e.target.name);
todo[e.target.name] = e.target.value
setTodo({...todo})
}
return (
<div className="flex flex-col space-y-4 w-96 mx-auto">
<label htmlFor="title" className="text-sm font-semibold text-gray-700">Title</label>
<input
type="text"
name="title"
className="border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-300"
placeholder="Enter Title"
value={todo.title}
onChange={e => handleChange(e)}
/>
<label htmlFor="writer" className="text-sm font-semibold text-gray-700">Writer</label>
<input
type="text"
name="writer"
className="border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-300"
placeholder="Enter Title"
value={todo.writer}
onChange={e => handleChange(e)}
/>
<label htmlFor="dueDate" className="text-sm font-semibold text-gray-700">DueDate</label>
<input
type="date"
name="dueDate"
className="border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-300"
placeholder="Enter Title"
value={todo.dueDate}
onChange={e => handleChange(e)}
/>
</div>
);
}
export default TodoInput;
๐ ํด๋น์ฝ๋์์ handleChange๋ฅผ ๋ณด๊ฒ ๋๋ฉด e(์ด๋ฒคํธ)๊ฐ ์๋๋ฐ, ํด๋น e์๋ ๋ค์ํ ์์ฑ์ค์ target์์ฑ์ด ์๊ณ , ๊ทธ๋ด๋ถ์๋ name ์์ฑ์ด ์๋ค.
๐ ์๋ return์ input ํ๊ทธ ๋ด๋ถ์ name๊ฐ์ด title์ด๋ฏ๋ก target์์ฑ์ name์์ฑ๋ํ title์ด ๋๋ค. ํ์ฌ, ํด๋น ์
๋ ฅ์ค์ธ input๊ฐ์ด title์ด๋ผ๋ฉด
todo[e.target.name] = e.target.value
todo[title] = value(์
๋ ฅ๋๋๊ฐ) ์ด ๋๋๊ฒ์ด๋ค.
export interface ITodo{
mno? : number,
title : string,
writer : string,
dueDate : string,
}
export interface IPageResponse{
content : ITodo[],
totalElements : number,
number : number,
first : boolean,
last : boolean,
size : number,
totalPages: number,
}
๐ typeScript๋ ๊ธฐ๋ณธ์ ์ผ๋ก type์ ๋ฏธ๋ฆฌ ๊ตฌ์ฑํด๋๊ณ ์์ํ๋๋ฐ, ํด๋น ํ์ ์ IpageResponse{ITodo[], totalElements..} ์ ํํ์ด๋ค.
import {IPageResponse, ITodo} from "../type/todo.ts";
import axios from "axios";
const host:string = 'http://localhost:8088/api/v1/todos'
export const getTodoList = async (page?:number, size?:number): Promise<IPageResponse> => {
const pageValue:number = page || 1 // ์์ผ๋ฉด 1ํ์ด์ง
const sizeValue:number = size || 10 // ์์ผ๋ฉด size๋ 10
const res = await axios.get(`${host}/list?page=${pageValue}&size=${sizeValue}`)
return res.data;
}
export const postTodo = async (todo:ITodo): Promise<number> => { // mno๊ฐ์ ๋ฐํํ๊ธฐ ์ํด return๊ฐ์ number
const res = await axios.post(`${host}`, todo) // ํด๋น url์ ํตํด todo ๋งค๊ฐ๋ณ์๊ฐ ์๋ฒ์ ์ ๋ฌ
return res.data.mno
}
๐ ํด๋น์ฝ๋๋ api์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ์ฝ๋๋ก docker์๋ฒ๋ฅผ host๋ก ์ก์๋๊ณ ,
getTodoList๋ฅผ ํตํด ๋งค๊ฐ๋ณ์๋ฅผ page์ size๋ก ํด์ ๋ฐ์ดํฐ๋ฅผ getํ์ฌ return๊ฐ์ผ๋ก๋ IPageResponse์ ํํ๋ก ๋ฐ์์จ๋ค.
๐ post๋ฅผ ํตํด api์๋ฒ์ todo์ ๋งค๊ฐ๋ณ์ ๊ฐ์ ๋ฃ์ด์ฃผ๊ณ return๊ฐ์ผ๋ก mno์ธ number๊ฐ์ ๋ฐ์์จ๋ค. - ์ด ๊ฐ์ ๋์ค์ modal์ฐฝ์์ "๋ช ๋ฒ์ด ๋ฑ๋ก๋์์ต๋๋ค" ์ ์ฌ์ฉ๋๋ค.
function LoadingComponent() {
return (
<div className="fixed inset-0 flex items-center justify-center bg-gray-900 bg-opacity-50 z-50">
<div className="bg-white p-8 rounded-lg shadow-lg w-96">
<h2 className="text-xl font-semibold mb-4">Loading</h2>
<p className="mb-6">Loading</p>
</div>
</div>
);
}
export default LoadingComponent;
๐ ํด๋น ์ฝ๋๋ input ํ์ด์ง ๋ด๋ถ์์ ๋ฑ๋ก์ด ๋ ์ดํ์ ์ ์์ ๋ก๋ฉํ์ด์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด commonํจํค์ง์ ๊ตฌ์ฑํ์๋ค.
import {IPageResponse} from "../../type/todo.ts";
interface Props {
pageResponse:IPageResponse;
changePage:(p:number) => void;
}
const makeArr = (from:number, to:number, prev:boolean, next:boolean):number[] => {
const arr:number[] = []
if(prev){
arr.push(from-1)
}
for (let i = from; i < to; i++) {
arr.push(i);
}
return arr
}
function PageComponent({pageResponse, changePage}:Props) {
const current: number = pageResponse.number + 1 // ํ์ฌํ์ด์ง 0 ๋ถํฐ์์ํ๋ฏ๋ก + 1
const tempLast: number = Math.ceil(current/10.0) * 10 // ํ์ฌ 11ํ์ด์ง์ผ๋ 20ํ์ด์ง๊น์ง ๋์ค๊ธฐ
const startPage: number = tempLast - 9
const endPage: number = pageResponse.totalPages < tempLast ? pageResponse.totalPages : tempLast;
//const prev: boolean = startPage !== 1
const pageNums:number[] = makeArr(startPage, endPage, false, false)
const lis = pageNums.map( num => <li
className='px-4 py-2 text-white bg-blue-500 border border-blue-500 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-300'
key={num}
onClick={() =>changePage(num)}
>
{num}
</li>)
return (
<div>
<ul className='flex justify-center items-center space-x-2 mt-6'>
{lis}
</ul>
</div>
);
}
export default PageComponent;
๐ ํด๋น์ฝ๋๋ page์ ๋ฒํผ ์ญํ ์ ํ๋ ๊ณตํต ์ฝ๋์ธ๋ฐ, PageComponent์ ๋งค๊ฐ๋ณ์ ๊ฐ์ผ๋ก pageResponse, changePage๋ฅผ ์ฌ์ฉํ๊ธฐ์ํด interface๋ก ์ ์ํ์๊ณ , ์ด๋ TodoList์์ ์ฌ์ฉ๋ ์์ ์ด๋ค.
interface ResultModalProps {
msg: string;
callback:() => void
}
function ResultModal({ msg, callback } :ResultModalProps) {
return (
<div className="fixed inset-0 flex items-center justify-center bg-gray-900 bg-opacity-50 z-50">
<div className="bg-orange-500-600 p-8 rounded-lg shadow-lg w-96">
<h2 className="text-xl font-semibold mb-4">Result</h2>
<p className="mb-6">{msg}</p>
<button className='bg-orange-500 text-white px-4 py-2 rounded hover:bg-orange-500'
onClick={() => callback()}
>
cancel
</button>
</div>
</div>
);
}
export default ResultModal;
๐ ํด๋น์ฝ๋๋ input ํ์ด์ง์์ ๋ฑ๋ก๋ฒํผ์ ๋๋ ์๋, ๋ช๋ฒ์ด ๋ฑ๋ก์ด๋๊ณ , callback์ ํ์ฌ input ๋ด๋ถ์ ์ฝ๋๋ฅผ ์ด๊ธฐํ ์์ผ์ฃผ๋ ์ฝ๋์ด๋ค.
import TodoList from "./TodoList.tsx";
import {ReactElement, useState} from "react";
import TodoInput from "./TodoInput.tsx";
function TodoIndex(): ReactElement {
const [page, setPage] = useState(1);
const [refresh, setRefresh] = useState(false);
const changePage = (pageNum: number) => {
setPage(pageNum);
setRefresh(!refresh);
}
return (
<div className='flex flex-col'>
<TodoInput changePage={changePage}></TodoInput>
<TodoList pageNum={page} refresh={refresh} changePage={changePage}></TodoList>
</div>
);
}
export default TodoIndex;
๐ ํด๋น ์ฝ๋๋ TodoInput, TodoList์ ๋ถ๋ชจ์ปดํฌ๋ํธ์ ์ญํ ์ํ๊ณ , page์ ๊ด๋ จ๋ page, refresh๋ฅผ list๊ฐ ์๋ ๋ถ๋ชจ์ปดํฌ๋ํธ์ ๋นผ๋๋๋ฐ, ์ด ์ด์ ๊ฐ ๋งค์ฐ ์ค์ํ๋ค.
๐ ๊ธฐ๋ณธ์ ์ผ๋ก TodoList์์ ํ์ด์ง ๊ตฌํ์ ํ๊ธฐ ๋๋ฌธ์ ํ์ด์ง์ ๊ด๋ จ๋ ์์ค์ฝ๋๋ค์ Listํ๊ทธ๋ด๋ถ๋ก ๋ฃ๊ฒ ๋๋๋ฐ, ์ด๋ ๊ฒ ๋๋ฉด ํ์ด์ง์ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ List์์ ๋ฐ์ ์ฌ์ฉํ์ง ๋ชปํ๋ค. ๊ณ ๋ก, Input ํ๊ทธ๋ด์์๋ ํ์ด์ง ์ด๋์ด ๋ถ๊ฐ๋ฅํ๋ค.
๐ Input์์๋ ํ์ด์ง ์ด๋์ด ํ์์๋ค๊ณ ์๊ฐํ ์ ์์ง๋ง, ๋ฑ๋ก๋ฒํผ์ ๋๋ฅด๊ณ ๋์ ๊ธฐ๋ณธ์ ์ผ๋ก 1ํ์ด์ง๋ก ๊ฐ๋๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค. ๊ทธ๋ฌ๋ฏ๋ก ํด๋น ๊ธฐ๋ฅ๊ณผ ๊ฐ์ด ์ด๋์ชฝ์ ๊ธฐ๋ฅ์ ๋ถ์ฌํ๋๊ฒ์ด ์ ๋งคํ๋ค๋ฉด ๋ถ๋ชจ์ชฝ์ผ๋ก ๋ถ์ฌํ๋๊ฒ์ด ํ๋นํ๋ค.
๐ return๋ด๋ถ์์๋ TodoInput์์๋ 1ํ์ด์ง๋ก ๊ฐ๋ ๊ธฐ๋ฅ์ด ํ์ํ๋ฏ๋ก changePage์ ๊ธฐ๋ฅ์ ๋ถ์ฌํ๊ณ , Listํ์ด์ง์์๋ page์ ๊ด๋ จ๋ ๋ชจ๋ ๊ธฐ๋ฅ๋ค์ ๋ถ์ฌํ๋ค. - ์ด๋ ๊ฒ ๋ถ์ฌํ๋๊ฒ์ props์ ํํ๋ก ๋ถ์ฌํ๋๊ฒ์ด๋ค.
import {ITodo} from "../../type/todo.ts";
import React, {ChangeEvent, useState} from "react";
import {postTodo} from "../../api/TodoAPI.ts";
import LoadingComponent from "../common/LoadingComponent.tsx";
import ResultModal from "../common/ResultModal.tsx";
const initState: ITodo = {
title : '',
writer : '',
dueDate : '',
}
interface TodoInputProps { // input ๋ฒํผ ์ดํ์ 1๋ฒํ์ด์ง ์ด๋ํ๊ธฐ ์ํด์
changePage: (p:number) => void
}
function TodoInput({changePage}:TodoInputProps) {
const [todo, setTodo] = useState<ITodo>({...initState}) // ๊ธฐ๋ณธ๊ฐ์ initalState
const [loading, setLoading] = useState(false);
const [resultData, setResultData] = useState<number>(0); // api์ post๋ฅผ ํตํด ๋ฑ๋ก๋๊ณ return๋๋ mno๊ฐ์ ์ ์ฅํ๊ธฐ ์ํ ์ํ
const handleChange= (e:ChangeEvent<HTMLInputElement>) => { // ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๊ฐ์ ๋ํ ์ด๋ฒคํธ
//console.log(e)
//console.log(e.target)
console.log(e.target.value);
console.log(e.target.name);
todo[e.target.name] = e.target.value
setTodo({...todo})
}
const handleClick = () => {
setLoading(true)
postTodo(todo).then(number => {
setLoading(false)
setResultData(number) // ๋ฑ๋ก์๋ฃ๋๊ณ return๋๋ mno๊ฐ์ ResultData์ ์ฃผ์
})
}
const clearResult=() => {
setResultData(0) // resultData ๊ฐ์ ์ด๊ธฐํ
setTodo(initState) // input ๋ด์ฉ ์ด๊ธฐํ
changePage(1) // 1๋ฒํ์ด์ง๋ก ์ด๋
}
return (
<div className="flex flex-col space-y-4 w-96 mx-auto">
{loading && <LoadingComponent></LoadingComponent>}
{resultData !== 0 && <ResultModal msg={`${resultData}๋ฒ ๋ฑ๋ก์๋ฃ`} callback={clearResult}/> }
<label className="text-sm font-semibold text-gray-700">Title</label>
<input
type="text"
name="title"
className="border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-300"
placeholder="Enter Title"
value={todo.title}
onChange={e => handleChange(e)}
/>
<label className="text-sm font-semibold text-gray-700">Writer</label>
<input
type="text"
name="writer"
className="border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-300"
placeholder="Enter Title"
value={todo.writer}
onChange={e => handleChange(e)}
/>
<label className="text-sm font-semibold text-gray-700">DueDate</label>
<input
type="date"
name="dueDate"
className="border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-300"
placeholder="Enter Title"
value={todo.dueDate}
onChange={e => handleChange(e)}
/>
<button
type="submit"
className="bg-blue-500 text-white font-semibold py-3 px-6 rounded-lg shadow-lg hover:bg-blue-600 focus:outline-none focus:ring-4 focus:ring-blue-300 transition duration-300"
onClick={handleClick}
>
Submit
</button>
</div>
);
}
export default TodoInput;
๐ ํด๋น์ฝ๋๋ inputํ์ด์ง๋ก input ํ๊ทธ์ ๋ค์ด๊ฐ์ผํ ๋ด์ฉ์ iniState๋ก ๋น ๊ฐ์ฒด์ ํํ๋ก ๊ตฌํํด๋๋๋ค. ์ด๋ ๊ฒ ํด๋์ผ๋ฉด ๋์ค์ callbackํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ด์ ์ ์์ฑํ๋ ๋ด์ฉ์ ์ด๊ธฐํ ํ ์ ์๋ค.
๐ TodoInput ๋ด๋ถ์ useState๊ฐ 3๊ฐ์๋๋ฐ, ๊ฐ๊ฐ์ ์ดํด๋ณด์๋ฉด todo๋ ์ฐ์ input ํ๊ทธ์ ์
๋ ฅํ ITodo๊ฐ์ฒด์ ํํ๋ก ์ ์ฅ๋๊ณ , APIPost๋ฅผ ํตํด ์๋ฒํต์ ์ ํ๋๊ฒ์ด๋ค.
loading์ ๊ธฐ๋ณธ false๋ก ๋์ด์๋๋ฐ, loading(true)์ผ๋ Loadingcomponent๊ฐ ์ ์ฉ๋์ด ๋ก๋ฉํ๋ฉด์ด ์ ์ ์ถ๋ ฅ์ด ๋๋ ์ญํ ์ด๊ณ , resultData๋ API์ POST๋ฅผ ํตํด INPUT์ ๋ด์ฉ์ด ์ ์ฅ์ด ๋์๊ณ , return๊ฐ์ผ๋ก mno์ธ number๊ฐ์ ๋ฐ๊ฒ๋๋๋ฐ ์ด number๊ฐ์ ์ ์ฅํ๊ฒ๋ ์ํ๊ฐ resultData์ด๋ค. resultData๋ฅผ ํตํด์ modal์ฐฝ์์ ์ฌ์ฉํ๊ฒ ๋๋ค.
import React, {ReactElement, useEffect, useState} from "react";
import {IPageResponse, ITodo} from "../../type/todo.ts";
import {getTodoList} from "../../api/TodoAPI.ts";
import LoadingComponent from "../common/LoadingComponent.tsx";
import PageComponent from "../common/PageComponent.tsx";
const initalState: IPageResponse = {
content : [],
first: false,
last: false,
number: 0,
size: 0,
totalElements: 0,
totalPages: 0
}
interface TodoListProps{
pageNum: number;
refresh: boolean;
changePage: (p:number) => void
}
function TodoList({pageNum, refresh, changePage} : TodoListProps): ReactElement {
const [pageData, setPageData] = useState<IPageResponse>(initalState);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
getTodoList(pageNum).then(data => {
setPageData(data)
setTimeout(()=> {
setLoading(false) // ๋ฐ์ดํฐ ๋ฐ๊ณ ๋ก๋ฉ์ฐฝ๊บผ์ง
}, 600)
})
},[pageNum, refresh]);
const TodoLI = pageData?.content?.map((todo:ITodo)=>{
return (
<li key={todo.mno}>
{todo.mno} -
{todo.title} -
{todo.writer} -
{todo.dueDate}
</li>
)
})
return (
<div>
{loading && <LoadingComponent></LoadingComponent>}
<ul>
{TodoLI}
</ul>
<div>
<PageComponent pageResponse={pageData} changePage={changePage}></PageComponent>
</div>
</div>
);
}
export default TodoList;
๐ useState์ ์ฒซ๋ฒ์งธ๋ pageData๋ก api์ get์ ํตํด ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ setPageData์ ํด๋น data๊ฐ์ ๋ฃ์ด pageData๋ก ์ถ๋ ฅํ๋ค.
๐ useEffect์ ์ญํ ์ ์กฐ๊ฑด์ด ๋ณํ ๋๋ง ๋๋๋ง์ ํ๋๊ฒ์ธ๋ฐ, pageNum, refesh๊ฐ์ฒด ์ฒ๋ผ ๋๊ฐ์ค ํ๋๊ฐ ๋ณ๊ฒฝ์ด ๋๋ฉด ํด๋น ๋ฉ์๋๊ฐ ์คํ๋๋ ๊ฒ.
๐ ์กฐ๊ฑด์ด ๋ณํ ๋๋ง ๋๋๋ง(ํ์ด์ง ๋ฒํธ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง)
busy waiting
๐ ๋์์๋ก busy waiting์ ์ค๋ช
ํ์๋ฉด, ํด๋น ๋ฐ์ดํฐ๊ฐ ์๋์ง ์์๋์ง ๊ณ์ ์ฒดํฌ ํ๊ธฐ์ํด while๋ฌธ์ ๋ฃ์ด์ ๋น๋๊ธฐ ์ฝ๋๊ฐ ๋์ฐฉํ๋ฉด ๋๋๋ง์ด ๋๋๋ก ๋ฌดํ๋ฃจํ๋ฅผ ๋๋๊ฒ์ด๋ค.
๐ ์ด๋ฌํ ๋ฌด์ํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์ง ์๊ธฐ ์ํด์ useEffect๋ฅผ ์ฌ์ฉํ๋ค.
useEffect(() => {
setLoading(true);
getTodoList(pageNum).then(data => {
setPageData(data)
setTimeout(()=> {
setLoading(false) // ๋ฐ์ดํฐ ๋ฐ๊ณ ๋ก๋ฉ์ฐฝ๊บผ์ง
}, 600)
})
},[pageNum, refresh]);
๐ pageNum || refresh๊ฐ์ฒด๊ฐ ๋ณํ๊ฒ ๋๋ฉด ์ฝ๋๊ฐ ์คํ๋จ
๐ ํ์ด์ง๋ฅผ ์คํํ ๋๋ง๋ค ๋ฉ์๋๋ฅผ ๊ณ์ ์คํํด์ผํ๋๊ฒ์ ๋ง๊ธฐ์ํจ.
const clearResult=() => {
setResultData(0) // resultData ๊ฐ์ ์ด๊ธฐํ
setTodo(initState) // input ๋ด์ฉ ์ด๊ธฐํ
changePage(1) // 1๋ฒํ์ด์ง๋ก ์ด๋
}
{resultData !== 0 && <ResultModal msg={`${resultData}๋ฒ ๋ฑ๋ก์๋ฃ`} callback={clearResult}/> }
๐ ๋ฑ๋ก๋ฒํผ์ ๋๋ฅด๊ณ ์ด์ ์ ์ฌ์ฉํ๋ ๋ด์ฉ์ ์๋์ผ๋ก ์ด๊ธฐํ ์ํค๋ ๊ธฐ๋ฅ์ ์ํจ.
๐ ์ฅ๋ฐ๊ตฌ๋์ ๋ด์ฉ์ด ๋ณ๊ฒฝ๋์์๋ ์ดํฉ์ ๋ค์ ๊ณ์ฐํด์ผํ๋ค๋ฉด ์ฌ์ฉ๋๋ ์ฝ๋
function MyComponent({ items }) {
const expensiveCalculation = (data) => {
// ๋ณต์กํ ๊ณ์ฐ
return data.reduce((acc, item) => acc + item.value, 0);
};
const memoizedValue = useMemo(() => expensiveCalculation(items), [items]);
return <div>{memoizedValue}</div>;
}
function TodoInput() {
const selectRef = useRef();
const handleClick = () => {
console.log(selectRef.current)
}
return (
<div>
<select ref={selectRef}>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
<option value='4'>4</option>
<option value='5'>5</option>
</select>
<button onClick={handleClick}>GET</button>
</div>
);
}
๐ useRef๋ฅผ ํตํด์ select๋ฒํผ์ ๋ง๋ค์ด ๋๊ณ
import TodoList from "./TodoList.jsx";
import TodoInput from "./TodoInput.jsx";
function TodoIndex() {
return (
<div>
<div className='text-4xl'>Todo Index Component</div>
<TodoInput></TodoInput>
<TodoList></TodoList>
<TodoInput></TodoInput>
</div>
);
}
export default TodoIndex;
๐ Index์ปดํฌ๋ํธ์์๋ Input์ ๋๋ฒ ํธ์ถํ๋ค. ์ด๋ select๋ ํ๋์ ๋ฉ์๋์ง๋ง ํธ์ถ์์ name, id์ ๊ฐ์ ๊ตฌ๋ณํ ์์๋ ๋ฐฉ๋ฒ์ด ์๋ ์ํ์ด์ง๋ง useRef๋ฅผ ํตํด์ ๊ฐ๊ฐ์ ๋ณ๋์ ๊ฐ์ ์ป์ด๋ผ ์ ์๋ค.
๐ vue์์์ provide inject์ ๊ฐ์ด ์ปดํฌ๋ํธ๊ฐ์ ์ํ๋ฅผ ๊ณต์ ํด์ฃผ๋ ๊ธฐ๋ฅ
import {useContext} from "react";
import {CountContext} from "./CountIndex.jsx";
function CountButtons() {
const {changeCount} = useContext(CountContext);
return (
<div>
<button onClick={changeCount}>PLUS</button>
</div>
);
}
export default CountButtons;
import {useContext} from "react";
import {CountContext} from "./CountIndex.jsx";
function CountDisplay() {
const {count} = useContext(CountContext);
return (
<div className="text-4xl">
{count}
</div>
);
}
export default CountDisplay;
import CountButtons from "./CountButtons.jsx";
import {createContext, useState} from "react";
import CountDisplay from "./CountDisplay.jsx";
export const CountContext = createContext() // context์์ฑํ์ฌ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ CountContext๋ผ๋ ์ด๋ฆ์ผ๋ก ์ฌ์ฉ๋๋ค.
function CountIndex() {
const [count, setCount] = useState(0);
const changeCount = () => {
setCount(count+1);
}
return ( // Provider๋ฅผ ํตํด์ ํ์ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ๋ค.
<CountContext.Provider value={{count, changeCount}}>
<CountDisplay></CountDisplay>
<CountButtons></CountButtons>
</CountContext.Provider>
);
}
export default CountIndex;
๐ createContext๋ฅผ ํตํด useContext๋ฅผ ์์ฑํ๊ณ , return๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ํ ๋นํ๋๊ณณ์์ CountContext.Provider์ ํํ์ ํ๊ทธ๋ก ํ ๋นํ ์ปดํฌ๋ํธ๋ค์ ๊ฐ์ผ๋ค.
๐ ์ด๋ ๊ฒ ๋๋ฉด count๋ณ์, changeCount๋ฉ์๋๋ค์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๊ฐ์ ์ํ๋ฅผ ๊ณต์ ํ ์ ์๋ค.
๐ ๋ฌผ๋ก ์ด๋๊น์งํ ๋ถ๋ถ๋ ์ํ๊ณต์ ์ง๋ง ๊ทธ๊ฒ๋ค์ props์ ํํ์ด๊ณ ์ด๊ฒ์ provider๋ฅผ ํตํด ๊ณต์ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.