목차 | |
1. | useReducer |
2. | 느낀 점 |
1. useReducer
◈ 이론
■ useReducer 요청 상태에 따라 관리하기
// action : dispatch 함수로 받을 액션
const reducer = ( state, action ) => {
switch(action.type){
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
}
}
// state : 현재 상태 값 => 변수
// dispatch : state 값을 업데이트 하고, 재랜더링을 촉발하는 함수
const [ state, dispatch ] = useReducer( reducer, 0 );
return (
<h3>{state}<h3>
<button onClick{() => { dispatch({ type: "INCREMENT"}) }}>+</button>
<button onClick{() => { dispatch({ type: "DECREMENT"}) }}>-</button>
)
▣ my-app
- Comp.jsx
import React from 'react';
import './comp.css';
import Counter from './Counter';
import Input from './Input';
import Color from './Color';
import Input2 from './Input2';
import Counter2 from './Counter2';
const comp2 = () => {
// Count 예제
// Hook : 기능을 할 수 있게 해주는 역할
// useState() : 상태변화
return (
<div className='comp2 comp'>
<div>comp2 영역입니다.</div>
<Counter2/>
<hr />
<Counter />
<hr />
<Input />
<hr />
<Color />
<hr />
<Input2 />
</div>
);
};
export default comp2;
- Counter2.jsx
import React, { useReducer } from 'react';
function reducer(state, action){
switch (action.type){
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
return state;
}
}
// const reducer = (state, action) => {}
const Counter2 = () => {
const [ count, dispatch ] = useReducer(reducer, 0);
const increment = () => {
dispatch({ type: "INCREMENT"});
}
return (
<div className='counter2'>
<h1>{count}</h1>
<button onClick={increment}>+</button>
<button onClick={() => {dispatch({ type: "DECREMENT"})}}>-</button>
</div>
);
};
export default Counter2;
▷ 출력
▣ axios-app
- App.js
import './App.css';
import User from './component/User';
import User2 from './component/User2';
function App() {
return (
<div className="App">
<User />
<hr />
<User2 />
</div>
);
}
export default App;
- User2.jsx
import React from 'react';
import { useEffect } from 'react';
import { useReducer } from 'react';
import axios from 'axios';
// useReducer로 요청 상태 관리하기
// action type : SUCCESS / ERROR / LOADING
function reducer( state, action ){
switch(action.type){
case 'LOADING':
return {
loading: true,
data: null,
error: null
};
case 'SUCCESS':
return {
loading: false,
data: action.data,
error: null
};
case 'ERROR':
return {
loading: false,
data: null,
error: action.error
};
default:
throw new Error(`Unhandled action type : ${action.type}`)
}
}
const User2 = () => {
const [ state, dispatch ] = useReducer(reducer, {
loading: false,
data: null,
error: null
});
// const [ users, setUsers ] = useState([]);
const fetchUsers = async () => {
try{
dispatch( { type : 'LOADING' } );
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
console.log(response);
// setUsers(response.data);
dispatch( { type : 'SUCCESS', data:response.data});
}catch(e){
dispatch( { type : 'ERROR', error: e } );
}
}
useEffect(() => {
fetchUsers();
},[]);
const { loading, data:users, error } = state; // state.date => users 키워드로 조회
if(loading) return <div>로딩중...</div>;
if(error) return <div>에러가 발생했습니다.</div>
if(!users) return <div>User null!!!</div>
return (
<div className='user2'>
<div>User2.jsx 영역</div>
<ul>
{
users.map(user => (
<li key={user.id}>{user.username} ({user.name})</li>
))
}
</ul>
<button onClick={fetchUsers}>다시 불러오기</button>
</div>
);
};
export default User2;
▷ 출력
▣ board-ui-app
- create-react-app board-ui-app
- cd board-ui-app
- npm i react-router-dom
- App.js
import './App.css';
import BoardHome from './component/BoardHome';
function App() {
return (
<div className="App">
<BoardHome />
</div>
);
}
export default App;
- BoardList.jsx
import React from 'react';
import { boardList } from '../data/data';
import '../component/board-style.css';
const BoardList = () => {
return (
<div className='boardList'>
<h2>Board List Page</h2>
<table>
<thead>
<tr className='subTitle'>
<th>번호</th>
<th>제목</th>
<th>내용</th>
<th>작성자</th>
<th>작성일</th>
</tr>
</thead>
<tbody>
{boardList.map(b => (
<tr key={b.id}>
<td className='center'>{b.id}</td>
<td>{b.title}</td>
<td>{b.contents}</td>
<td>{b.writer}</td>
<td>{b.reg_date}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default BoardList;
- BoardHome.jsx
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import BoardList from './BoardList';
import '../component/board-style.css';
const BoardHome = () => {
return (
<div className='boardHome'>
<h1 className='title'>My First React Board Project</h1>
<BrowserRouter>
<Routes>
<Route path='/' element={<BoardList/>}/>
<Route path='/list' element={<BoardList/>}/>
<Route />
<Route />
</Routes>
</BrowserRouter>
<button className='textButton'>글쓰기</button>
</div>
);
};
export default BoardHome;
- board-style.css
.boardList {
font-family: Arial, sans-serif;
margin: 20px;
}
.title{
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: transparent;
-webkit-background-clip: text;
margin-bottom: 30px;
}
h2 {
text-align: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: transparent;
-webkit-background-clip: text;
margin-bottom: 30px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.subTitle{
background: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
}
.subTitle>th{
color: #764ba2;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.center{
text-align: center;
}
th {
color: #333;
}
tr:nth-child(even) {
background-color: #F9F9F9;
}
tr:hover {
background: linear-gradient(to right, rgba(135, 207, 235, 0.452), rgba(255, 192, 203, 0.479));
color: #667eea;
font-weight: 700;
}
table {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
th:nth-child(1), td:nth-child(1) {
width: 3%;
word-break: break-word;
}
.textButton{
width: 150px;
height: 60px;
color: white;
font-size: 20px;
font-weight: 700;
/* border 그라데이션 - button */
border: 1px solid transparent;
border-radius: 20px;
background-image: linear-gradient(to right top, #12c2e975, #c471ed7a, #f64f5a8c);
background-origin: border-box;
background-clip: content-box, border-box;
}
.textButton:hover{
cursor: pointer;
transform: scale(1.05);
}
.textbutton:active {
transform: scale(0.95);
}
- data.js
export const boardList =
[
{
"id": 7,
"title": "React란 무엇인가요?",
"contents": "React는 몇 가지 종류의 컴포넌트를 가지지만 우리는 React.Component의 하위 클래스를 사용해보겠습니다.",
"writer": "purple",
"reg_date": "2024-05-25T07:41:21.000Z"
},
{
"id": 6,
"title": "브라우저에 코드 작성하기",
"contents": "먼저 새 탭에서 초기 코드를 열어주세요. 새 탭은 비어있는 틱택토 게임판과 React 코드를 보여줄 것입니다. 우리는 자습서에서 React 코드를 편집할 것입니다.",
"writer": "navy",
"reg_date": "2024-05-25T07:41:20.000Z"
},
{
"id": 5,
"title": "자습서를 시작하기 전에",
"contents": "우리는 이 자습서에서 작은 게임을 만들겁니다. 게임을 만들고 싶지 않아서 자습서를 건너뛰고 싶을 수 있습니다. 그래도 한번 해보세요!",
"writer": "blue",
"reg_date": "2024-05-25T07:41:20.000Z"
},
{
"id": 4,
"title": "외부 플러그인을 사용하는 컴포넌트",
"contents": "React는 유연하며 다른 라이브러리나 프레임워크를 함께 활용할 수 있습니다. 이 예제에서는 외부 마크다운 라이브러리인 remarkable을 사용해 \u003Ctextarea\u003E의 값을 실시간으로 변환합니다.",
"writer": "green",
"reg_date": "2024-05-25T07:41:19.000Z"
},
{
"id": 3,
"title": "애플리케이션",
"contents": "props와 state를 사용해서 간단한 Todo 애플리케이션을 만들 수 있습니다.",
"writer": "yellow",
"reg_date": "2024-05-25T07:41:19.000Z"
},
{
"id": 2,
"title": "상태를 가지는 컴포넌트",
"contents": "컴포넌트는 this.props를 이용해 입력 데이터를 다루는 것 외에도 내부적인 상태 데이터를 가질 수 있습니다. 이는 this.state로 접근할 수 있습니다.",
"writer": "orange",
"reg_date": "2024-05-25T07:41:19.000Z"
},
{
"id": 1,
"title": "한 번 배워서 어디서나 사용하기",
"contents": "기술 스택의 나머지 부분에는 관여하지 않기 때문에, 기존 코드를 다시 작성하지 않고도 React의 새로운 기능을 이용해 개발할 수 있습니다.",
"writer": "red",
"reg_date": "2024-05-25T07:41:17.000Z"
}
]
▷ 출력
2. 느낀 점
DB와 react를 활용해서 todoList를 잘 만들 수 있을 지 너무 고민이 되고 DB와 react를 까먹지 않도록 복습을 해야 한다고 생각하게 되었다. 특히 react의 param, axios, useEffect, useReducer를 헷갈리지 않도록 신경써야 할 것 같다.
'Front-end > React' 카테고리의 다른 글
React 기초(게시판)- AWS 풀스택 과정 43일차 (0) | 2024.09.20 |
---|---|
React 기초(db 연동)- AWS 풀스택 과정 42일차 (0) | 2024.09.19 |
React 기초(todoList)- AWS 풀스택 과정 40일차 (0) | 2024.09.09 |
React 기초(배열)- AWS 풀스택 과정 39일차 (0) | 2024.09.06 |
React 기초(input, param)- AWS 풀스택 과정 38일차 (0) | 2024.09.05 |