Front-end/React

React 기초(useReducer)- AWS 풀스택 과정 41일차

awspspgh 2024. 9. 10. 13:50
목차
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;

 

▷ 출력

출력 (1)

 

▣ 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;

 

▷ 출력

출력 (2)

 

▣ 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"
    }
  ]

 

▷ 출력

출력 (3)

 

2. 느낀 점

DB와 react를 활용해서 todoList를 잘 만들 수 있을 지 너무 고민이 되고 DB와 react를 까먹지 않도록 복습을 해야 한다고 생각하게 되었다. 특히 react의 param, axios, useEffect, useReducer를 헷갈리지 않도록 신경써야 할 것 같다.