반응형
저장없이 배열에 내용을 추가, 읽기, 수정, 삭제하는 내용입니다.
버튼을 조각조각 내서 재사용성을 늘렸습니다.
내부 트리
├─ src
│ ├─ App.css
│ ├─ App.tsx
│ ├─ ProtectRouter.tsx
│ ├─ components
│ │ ├─ crud
│ │ │ └─ CRUD.tsx
│ │ ├─ item
│ │ │ ├─ CreateButton.tsx
│ │ │ ├─ TextInput.tsx
│ │ │ ├─ Textarea.tsx
│ │ │ └─ UpdateButton.tsx
│ │ └─ ts
│ │ └─ interface.ts
│ ├─ index.tsx
│ ├─ react-app-env.d.ts
│ └─ styles
│ └─ CRUD.module.scss
└─ tsconfig.json
ts
./ts/interface.ts
import { Dispatch, SetStateAction } from "react";
export interface TextProps {
text: string,
setText: Dispatch<SetStateAction<string>>
}
export interface ButtonProps {
btn: boolean,
name: string,
setBtn: Dispatch<SetStateAction<boolean>>
}
export interface ArrayProps {
id: number,
title: string,
contents: string,
}
item
./item/CreateButton.tsx
import React from "react";
import { ButtonProps } from "../ts/interface";
export const CreateButton: React.FunctionComponent<ButtonProps> = ({ name, btn, setBtn}) => {
const onClick = () => {
setBtn(true);
}
return (
<>
<button onClick={onClick}>{name}</button>
</>
)
};
./item/Textarea.tsx
import React from "react";
import { TextProps } from "../ts/interface";
export const Textarea: React.FunctionComponent<TextProps> = ({ text, setText }) => {
const onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const { target: { value } } = e;
setText(value);
}
return (
<>
<textarea value={text} onChange={onChange} placeholder="내용을 입력해주세요." />
</>
);
};
./item/TextInput.tsx
import React from "react";
import { TextProps } from "../ts/interface";
export const TextInput: React.FunctionComponent<TextProps> = ({ text, setText }) => {
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { target: { value } } = e;
setText(value);
}
return (
<>
<input type="text" value={text} onChange={onChange} placeholder="제목을 입력해주세요." />
</>
);
};
./item/UpdateButton.tsx
import React from "react";
import { ButtonProps } from "../ts/interface";
export const UpdateButton: React.FunctionComponent<ButtonProps> = ({ name, btn, setBtn}) => {
const onClick = () => {
setBtn(true);
}
return (
<>
<button onClick={onClick}>{name}</button>
</>
)
};
crud
./crud/CRUD.tsx
import { useCallback, useEffect, useState } from "react";
import styles from "../../styles/CRUD.module.scss";
import { CreateButton } from "../item/CreateButton";
import { Textarea } from "../item/Textarea";
import { TextInput } from "../item/TextInput";
import { UpdateButton } from "../item/UpdateButton";
import { ArrayProps } from "../ts/interface";
export const CRUD = () => {
const [title, setTitle] = useState<string>("");
const [contents, setContents] = useState<string>("");
// false: createBtn | true: updateBtn
const [btnChange, setBtnChange] = useState<boolean>(false);
const [createBtn, setCreateBtn] = useState<boolean>(false);
const [updateBtn, setUpdateBtn] = useState<boolean>(false);
const [array, setArray] = useState<ArrayProps[]>([]);
const [changeCount, setChangeCount] = useState<number>(0);
// 계속 증가하는 id Number
const [count, setCount] = useState<number>(0);
// 글 생성
const list = useCallback(() => {
setArray(prev => [...prev, {id: count, title: title, contents: contents}]);
setCount(prev => prev + 1);
setTitle("");
setContents("");
setCreateBtn(false);
}, [title, contents, count]);
useEffect(() => {
if (createBtn) list();
}, [createBtn, list]);
// 글 업데이트
useEffect(() => {
if (updateBtn) {
let update = [...array];
update[changeCount].title = title;
update[changeCount].contents = contents;
setArray(update);
setTitle("");
setContents("");
setBtnChange(false);
setUpdateBtn(false);
}
}, [updateBtn, array, title, contents, changeCount])
// 삭제 | 업데이트
const onUpdate = (list: string, item: ArrayProps) => {
if (list === "delete") setArray(array.filter(arr => arr.id !== item.id));
else if (list === "update") {
setChangeCount(item.id);
setTitle(item.title);
setContents(item.contents);
setBtnChange(true);
}
}
return (
<article className={styles.crud_wrap}>
<div className={styles.text_wrap}>
<TextInput text={title} setText={setTitle} />
</div>
<div className={styles.contents_wrap}>
<Textarea text={contents} setText={setContents} />
</div>
<div className={styles.button_wrap}>
{
btnChange ? <UpdateButton name="업데이트" btn={updateBtn} setBtn={setUpdateBtn} /> : <CreateButton name="확인" btn={createBtn} setBtn={setCreateBtn} />
}
</div>
<ul>
{
array.length > 0 && array.map(item => (
<li key={item.id}>
<h2>{item.title}
<div className={styles.arr_wrap}>
<span onClick={() => onUpdate("update", item)}>업데이트 | </span>
<span onClick={() => onUpdate("delete", item)}>삭제</span>
</div>
</h2>
<p>{item.contents}</p>
</li>
))
}
</ul>
</article>
);
}
App.tsx
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
// Components
import { CRUD } from './components/crud/CRUD';
function App() {
return (
<Router>
<Routes>
<Route path="/crud" element={<CRUD />} />
</Routes>
</Router>
);
}
export default App;
styles
./styles/CRUD.module.scss
* {
box-sizing: border-box;
}
.crud_wrap {
width: 100%;
max-width: 500px;
margin: 0 auto;
border: 2px solid #DDD;
border-radius: 16px;
padding: 2rem;
box-sizing: border-box;
> div {
margin-bottom: 1rem;
}
.text_wrap {
> input { width: 100%; padding: .5rem; border: 1px solid #DDD; border-radius: 16px; }
}
.contents_wrap {
> textarea { width: 100%; padding: .5rem; border: 1px solid #DDD; border-radius: 16px; resize: none; }
}
.button_wrap {
margin-top: 2rem;
> button { width: 100%; padding: .5rem; background: #DDD; border-radius: 16px; border: 0; color: #FFF; font-size: 16px; font-weight: 700; }
}
> ul {
list-style: none;
padding: 0;
> li {
> h2 {
position: relative;
font-size: 1.2rem;
.arr_wrap {
position: absolute;
top: 0;
right: 0;
> span {
font-size: .8rem;
font-weight: 500;
cursor: pointer;
}
}
}
> p { font-size: 1rem; }
}
}
}
반응형
'React > React' 카테고리의 다른 글
[ React ] Redux Toolkit 연습하기 ( 2 ) (0) | 2023.01.26 |
---|---|
[ React ] Redux Toolkit 연습하기 ( 1 ) (0) | 2023.01.26 |
[ React ] React Google 연동하기 (0) | 2023.01.02 |
[ React ] IntrinsicAttributes & AuthProps Error (0) | 2022.12.21 |
[ React ] redux 데이터 스토리지에 저장하기 ( redux-persist ) (0) | 2022.11.29 |