23/11/28 til 리덕스 타임어택

2023. 11. 29. 07:57카테고리 없음

2시간 이내에 투두리스트를 리덕스로 동작하게하기

 

<configstore.js>  // 일단 터미널에 yarn add redux react-redux  를 받는다.

import { combineReducers, createStore } from "redux";   // 일단 터미널에 yarn add redux react-redux  
import todos from "../modules/todos";                      //리덕스에서 컴바인리듀서와 크리에이트리듀서 임포트

const rootReducer = combineReducers({ todos });   //컴바인 리듀서는 모든 리듀서들을 {a,b,c} 같은객체를
                                                                                     //인자로 받아서 합친다.
const store = createStore(rootReducer);   //합친 리듀서들로 크리에이트스토어를 통해 스토어생성

export default store;         // 스토어를 익스포트한다.

 

<todos.js> 리듀서와 액션크리에이트등을 담는 파일

import { type } from "@testing-library/user-event/dist/type";

const initialState = [       // state의 초기상태 
  {
    id: "아이디1",
    title: "제목1",
    contents: "내용1",
    isDone: true,
  },
  {
    id: "아이디2",
    title: "제목2",
    contents: "내용2",
    isDone: false,
  },
  {
    id: "아이디3",
    title: "제목3",
    contents: "내용3",
    isDone: false,
  },
];

const ADD_TODO = "todos/ADD_TODO";                     //코드의 휴먼에러를 줄이기 위해서?
const DELETE_TODO = "todos/EDIT_TODO";              //상수의 변수명 선언
const SWITCH_TODO = "todos/SWITCH_TODO";

export const addTodo = (payload) => {          // 이 형태들이 액션 크리에이터객체 들이다
  return { type: ADD_TODO, payload };             //나중에 익스포트해서 다른 jsx파일 등에서 
};
export const deleteTodo = (payload) => {             //이 함수들을 호출해서 사용할 예정
  return { type: DELETE_TODO, payload };          //호출시에 인자를 payload로써 받아서
};
export const switchTodo = (payload) => {            //그 페이로드를 요소로 같는 객체를 반환한다.
  return { type: SWITCH_TODO, payload };         //객체의 요소는 위에서 작성한 타임과 페이로드가 있다.
};

const todos = (state = initialState, action) => {     //이게 리듀서 인자로 현재 상태인 스테이트와 액션을 
  switch (action.type) {                                    //받는다. 액션은 두개의 요소를 갖는데 타입과 페이로드이다.
    case ADD_TODO:
      const newtodo = action.payload;           //위에 액션크리에이트객체가 호출될시 받은 타입
      return [newtodo, ...state];                      //타입에 따라 스위치문의 case가 달라지게 되고 
    case DELETE_TODO:                         //해당케이스에서 받아놨던 payload값이 로직에 적용되여 
      const todoId = action.payload;                                     //결과 값을 리턴한다.
      return state.filter((todo) => todo.id !== todoId);
    case SWITCH_TODO:
      const { id, isDone } = action.payload;                        //삭제로직과 스위치 로직이 작동 안됨 ㅜㅜ
      return state.map((todo) => {
        if (todo.id === id) {                                                     
          return { ...todo, isDone: !isDone };
        }
      });

    default:
      return state;
  }
};

export default todos;

 

<Router.jsx>   리액트 라우터 돔을 터미널에서 받고 사용한다.

import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";        // 페이지는 홈과 디테일을 쓸 예정이라 두개를 임포트
import Detail from "../pages/Detail";     //홈과 디테일 페이지는 만들예정
 
export default function Router() {
  return (
    <>
      <BrowserRouter>             //브라우저라우터 라우츠 라우트는 아래처럼 쓴다.
        <Routes>
          <Route path="/" element={<Home />}></Route>      
          <Route path="/:id" element={<Detail />}></Route>      //사실 /:id 이거 잘 못쓰겠음 ㅜㅜ
        </Routes>
      </BrowserRouter>
    </>
  );
}

 

 

<index.js>   리덕스를 사용하기위한 세팅

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";                          //리액트리덕스에서 프로바이더 임포트
import store from "./config/configstore";                //내가만든 스토어 임포트

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>      //app 작스를 프로바이더 태그로 감싸주고 스토어 를 저런 형식으로
    <App />                  //App에게 넘긴다.
  </Provider>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

<Home.jsx>  메인 페이지이다.

import { nanoid } from "nanoid";                     // 나노아이디를 사용
import React, { useState } from "react";

import { useDispatch, useSelector } from "react-redux";
import { addTodo, deleteTodo, switchTodo } from "../modules/todos";   //액션크리에이터객체 임포트
import { useNavigate } from "react-router-dom";                                 // 사용할 꺼니까
                                                                              //화면 이동을 위한 유즈네이게이트
export default function Home() {
  const [title, setTitle] = useState("");                    //타이틀과 컨텐츠는 여기서만 사용할꺼니까
  const [contents, setContents] = useState("");     //간단하게 유즈스테이트로 인풋값에서 받아옴
  const navigate = useNavigate();
  const disPatch = useDispatch();                                //유즈디스페치 이 훅을 통해 액션크리에이터를 동작
  const todos = useSelector((state) => state.todos);        //하여 화면을 리랜더링한다.
                                                                               //유즈 셀렉터 이 것을통해 스토어에서 정보를 가져온다.
  const doingtodo = todos.filter(function (todo) {
    return todo.isDone === false;
  });
  const donetodo = todos.filter(function (todo) {
    return todo.isDone === true;
  });
  return (
    <>
      <h1>투두리스트</h1>
      <div>
        <form
          onSubmit={(e) => {
            e.preventDefault();

            const newTodo = {
              id: nanoid(),
              title: title,
              contents,
              isDone: false,
            };

            disPatch(addTodo(newTodo));
          }}
        >
          <span>제목</span>
          <input
            value={title}
            onChange={(e) => {
              setTitle(e.target.value);
            }}
          />
          <span>내용</span>
          <input
            value={contents}
            onChange={(e) => {
              setContents(e.target.value);
            }}
          />
          <button>추가하기</button>
        </form>
      </div>

      <div>
        <h1>해야할 투두</h1>
        <>
          {doingtodo.map((todo) => {
            return (
              <section>
                <div>id :{todo.id}</div>
                <div>제목 :{todo.title}</div>
                <div>내용 :{todo.contents}</div>
                <div>진행상태 :{todo.isDone ? "완료" : "진행중"}</div>

                <div>
                  <button
                    onClick={() => {
                      disPatch(deleteTodo(todo.id));           //이런식으로 디스패치에
                  >                                                      //액션객체를 넣고 액션객체에 patload를 넘긴다.
                    삭제하기
                  </button>
                  <button
                    onClick={() => {
                      disPatch(switchTodo(todo));    //페이로드는 어떤 case를 동작할꺼냐에 따라
                    }}                                               //받을게 달라진ㄴ다.
                  >
                    완료
                  </button>
                  <button
                    onClick={() => {
                      navigate(`/${todo.id}`);        //네비게이트 이렇게 쓰는거 맞나? 
                    }}                                             //이거 공부 다시해야되는데 아는사람 댓글좀 주세요
                  >
                    상세 페이지                          //브라우저에서 상세페이지 이동이 작동 안함 ㅜㅜ 망함
                  </button>
                </div>
              </section>
            );
          })}{" "}
        </>
      </div>
      <div>
        <h1>완료된 투두</h1>
        <>
          {donetodo.map((todo) => {
            return (
              <section>
                <div>id :{todo.id}</div>
                <div>제목 :{todo.title}</div>
                <div>내용 :{todo.contents}</div>
                <div>진행상태 :{todo.isDone ? "완료" : "진행중"}</div>
                <div>
                  <button
                    onClick={() => {
                      disPatch(deleteTodo(todo.id));
                    }}
                  >
                    삭제하기
                  </button>
                  <button
                    onClick={() => {
                      disPatch(switchTodo(todo));
                    }}
                  >
                    진행
                  </button>
                  <button
                    onClick={() => {
                      navigate(`/${todo.id}`);
                    }}
                  >
                    상세 페이지
                  </button>
                </div>
              </section>
            );
          })}{" "}
        </>
      </div>
    </>
  );
}

 

<Detail.jsx> 투두들의 상세 페이지

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import { deleteTodo, switchTodo } from "../modules/todos";

export default function Detail() {
  const todos = useSelector((state) => state.todos);
  const location = useLocation();
  const params = useParams();                   //아니 진짜 유즈 파람스 다시 해봐야하는데 .
  const navigate = useNavigate();
  const disPatch = useDispatch();        
  return (
    <>
      <div>
        {todos.id === params
          ? todos.map((todo) => {
              return (
                <>
                  <div>id :{todo.id}</div>
                  <div>제목 :{todo.title}</div>
                  <div>내용 :{todo.contents}</div>
                  <div>진행상태 :{todo.isDone ? "완료" : "진행중"}</div>

                  <div>
                    <button
                      onClick={() => {
                        disPatch(deleteTodo(todo.id));

                        navigate("/");
                      }}
                    >
                      삭제하기
                    </button>
                    <button
                      onClick={() => {
                        disPatch(switchTodo(todo));
                      }}
                    >
                      이동하기
                    </button>
                  </div>
                </>
              );
            })
          : null}
      </div>

      <div>
        <button
          onClickCapture={() => {
            navigate("/");
          }}
        >
          이전화면으로
        </button>
      </div>
    </>
  );
}

 

타임어택으로 리덕스를 해보긴했는데 이해도는 높아졌는데 숙련도가 너무 낮다. 큰일이다.