나의 개발일지

[React] Redux로 TodoList 만들기 본문

React/Redux

[React] Redux로 TodoList 만들기

heew0n 2023. 11. 28. 19:49

폴더 구조

 

<module>

todos.js

import uuid from "react-uuid";

const initialState = [
  {
    id: uuid(),
    title: "리액트",
    content: "이해하기",
    isDone: false,
  },
  {
    id: uuid(),
    title: "리덕스",
    content: "사용하기",
    isDone: false,
  },
  {
    id: uuid(),
    title: "열심히",
    content: "공부하기",
    isDone: true,
  },
];

// action items
const ADD_TODO = "ADD_TODO";
const DELETE_TODO = "DELETE_TODO";
const SWITCH_TODO = "SWITCH_TODO";


// action creator
// action은 객체, type과 payload를 가지고 있다
export const addTodo = (payload) => {
  return {
    type: ADD_TODO,
    payload,
  };
};

export const deleteTodo = (payload) => {
  return {
    type: DELETE_TODO,
    payload,
  };
};

export const switchTodo = (payload) => {
  return {
    type: SWITCH_TODO,
    payload,
  };
};

const todos = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      return [...state, action.payload];
    case DELETE_TODO:
      return state.filter((item) => item.id !== action.payload);
    case SWITCH_TODO:
      return state.map((item) => {
        if (item.id === action.payload) {
          return { ...item, isDone: !item.isDone };
        } else {
          return item;
        }
      });
    default:
      return state;
  }
};

export default todos;

 

 

 

action items 

휴먼 에러를 방지하기 위해 문자를 변수 선언 해준다

 

action creator

type과 payload를 가지고 있다. payload는 type에 따라 얼만큼 바뀌어야 하는지를 나타낸다

 

reducer

함수이다. switch문으로 case에 따라 각 다른 type을 가지고 있다

 

 

 

 

 

Home.jsx

import React from "react";
import Input from "../component/Input";
import TodoList from "../component/TodoList";
import Footer from "../component/Footer";
const Home = () => {
  return (
    <div>
      <Input />
      <TodoList isActive={true} />
      <TodoList isActive={false} />
      <Footer />
    </div>
  );
};

export default Home;

 

 

isActive 에 불리언 타입으로 false 또는 true를 주어서

TodoList의 삼항연산자를 사용할 때 쓴다 

 

 

 

 

 

Input.jsx

export default function Input() {
  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  const dispatch = useDispatch();

  const onHandler = (e) => {
    e.preventDefault();

    const newTodo = {
      id: uuid(),
      title,
      content,
      isDone: false,
    };

    dispatch(addTodo(newTodo));
  };

  const onTitleChangeHandler = (e) => {
    setTitle(e.target.value);
  };

  const onContentChangeHandler = (e) => {
    setContent(e.target.value);
  };

  return (
    <InputContainer>
      <form onSubmit={onHandler}>
        <InputTitleBox>
          제목 :
          <input
            type="text"
            value={title}
            onChange={onTitleChangeHandler}
          ></input>
        </InputTitleBox>
        <InputContentBox>
          내용 :
          <input
            type="text"
            value={content}
            onChange={onContentChangeHandler}
          ></input>
          <button type="submit">추가</button>
        </InputContentBox>
      </form>
    </InputContainer>
  );
}

 

 

 

addTodo 리듀서 사용!

함수 안에서 실행되어야 한다!!!

왜 강조하냐고..? 나도 알고 싶진 않았다...

무한 로딩에 빠지게 된다...

 

input 창에 글을 쓸 때마다 렌더링이 될 것이다

 

 

    const newTodo = {
      id: uuid(),
      title,
      content,
      isDone: false,
    };

 

새로운 변수를 선언하고 객체로 할당해준다

input 창에 글을 쓰면 저 객체의 형식으로 값이 들어간다

isDone이 false이므로 입력값은 해야할 일에 들어간다

 

 

 

 

 

 

TodoList.jsx

export default function TodoList({ isActive }) {
  // store에 있는 todos를 가지고 온다
  // state 는 store에 있는 모든 모듈을 가져오기 때문에
  // 해당되는 모듈만 들고와야 한다 state.todos
  const todos = useSelector((state) => state.todos);
  //   console.log(todos);

  const dispatch = useDispatch();
  const navigate = useNavigate();
  const ondeleteClick = (id) => {
    dispatch(deleteTodo(id));
  };

  const HandelSwitchBtn = (id) => {
    dispatch(switchTodo(id));
  };

  return (
    <StyledListBox>
      <h4>{isActive ? "해야할 일💥" : "완료된 일💡"}</h4>
      {todos
        .filter((data) => data.isDone === !isActive)
        .map((item) => {
          return (
            <>
              <StyledTodoBox key={item.id}>
                <h4>{item.title}</h4>
                <div>{item.content}</div>

                <button onClick={() => HandelSwitchBtn(item.id)}>
                  {isActive ? "완료" : "취소"}
                </button>
                <button onClick={() => ondeleteClick(item.id)}>삭제</button>
                <br />
                <button
                  onClick={() => {
                    navigate(`/${item.id}`);
                  }}
                >
                  상세보기
                </button>
              </StyledTodoBox>
            </>
          );
        })}
    </StyledListBox>
  );
}

const StyledListBox = styled.div`
  background-color: lightblue;
  padding: 20px;
`;

const StyledTodoBox = styled.div`
  background-color: lightgray;
  padding: 20px;
  margin: 10px;
`;

 

 

isActive :

Home으로부터 import를 받아 props를 해준다

{isActive ? "해야할 일💥" : "완료된 일💡"} 

true일 땐 해야할 일 list로 , false일 땐 완료된 일 list로 나눠진다

 

 

 

 

 

Detail.jsx

const Detail = () => {
  const navigate = useNavigate();
  const paramId = useParams().id;
  const todos = useSelector((state) => state.todos);
  const filteredTodo = todos.filter((item) => item.id === paramId)[0];
  return (
    <StDetailBox>
      <div>Detail</div>
      <h2 styled={{ marginBottom: "10px" }}>상세페이지</h2>
      <br />
      제목 : {filteredTodo.title}
      <br />
      내용 : {filteredTodo.content}
      <br />
      <button
        onClick={() => {
          navigate("/");
        }}
      >
        이전 페이지 이동
      </button>
    </StDetailBox>
  );
};

export default Detail;

 

 

 

useParams는 파마미터 값을 URL을 통해서 넘겨받은 페이지에서 사용할 수있도록 하는 Hook이다

해당 id값의 페이지로 넘어가게 해준다

 

const filteredTodo = todos.filter((item) => item.id === paramId)[0];

paramId와 todos의 id 값이 맞는 것을 필터링한다