본문 바로가기
개발 공부 시리즈/Redux-ToolKit

[RTK] Typescript todoList 만들기 - (3) 삭제 기능 만들기

by 그레이웅 2022. 10. 27. 01:50
반응형

[RTK] Typescript todoList 만들기 - (3) 삭제 기능 만들기

 

이전 포스팅에서는 추가 기능, material UI를 사용하여 디자인 등을 만들었다.

간단한 TodoListApp에 여러가지 설정 및 기능 등을 적용하면서 계속 구현해 볼 계획이다.

 

이전 포스팅

- 2022.10.25 - [개발 공부 시리즈/Redux-ToolKit] - [RTK] Typescript todoList 만들기 - (1)

- 2022.10.26 - [개발 공부 시리즈/Redux-ToolKit] - [RTK] Typescript todoList 만들기 - (2) 기본적인 디자인하기

 

 

이번 포스팅은 삭제 기능을 구현하도록 한다.

 

현재 TodoList는 카드 형태로 구현이 되어있다.

휴지통 버튼을 누르면 삭제가 되도록 Input의 내용이 삭제되도록 구현하겠다.

 

삭제하는 기능은 매우 간단하다.

 


1. todoSlice에 deleteTodo reducer 만들기

 

features/todos/todoSlice.ts

 

//todoSlice.ts

import { createSlice , PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'
import { v4 as uuidv4 } from 'uuid';

// Define a type for the slice state
interface Todo {
  id : string;
  text : string;
  completed : boolean
}

// Define the initial state using that type
const initialState: Todo[] = [
    {
        id : uuidv4(),
        text : '테스트',
        completed : false
    }
];

export const todoSlice = createSlice({
  name: 'todos',
  // `createSlice` will infer the state type from the `initialState` argument
  initialState,
  reducers: {
    addTodo: (state , action: PayloadAction<string>) => {
        // const { text } = action.payload;
        state.push({id : uuidv4(), text : action.payload, completed : false });
    },
    
    //삭제 기능 추가
    deleteTodo : (state , action : PayloadAction<string>) => {
        return state.filter((item) => item.id !== action.payload);
    },
  },
})

//export action에도 추가해 줌
export const { addTodo , deleteTodo} = todoSlice.actions

// Other code such as selectors can use the imported `RootState` type
export const selectTodos = (state: RootState) => state.todos

export default todoSlice.reducer

 

reduecers에 deleteTodo를 추가한다.

현재 todolist에서 uuid()로 만든 아이디 값이 payload로 들어온 값과 같지 않은 것 들만 state에 올리게 된다.

 

deleteTodo의 객체를 삭제하는 것을 설명하자면, 

filter함수를 사용하여 만약 Todo의 객체의 아이디가 1 인것을 삭제하고 한다고 가정한다.

1은 action.payload에 들어오게 되고 Todo전체 객체에서 아이디가 1인것을 제외하고 전체 todos 객체가 다시 만들어지게 된다.

 

 


2. onClick 함수 작성 및 dispatch 작성

 

App.tsx 파일에 todoSlice에서 만들어 준 deleteTodo의 action을 가져온다.

 

import { addTodo , deleteTodo} from './features/todos/todoSlice';

 

그리고 해당 카드 형태의 ListItem의 onClick 함수를 추가해주고 dispatch 시켜준다.

 

	//.....
    <List
              dense
              sx={{
                margin: "0 auto",
                width: "100%",
                maxWidth: 360,
                bgcolor: "background.paper",
              }}
            >
              {todoList.map((item) => (
                <Card style={{ marginTop: "20px" }}>
                  <CardContent>
                    <ListItem
                      style={{ justifyContent: "space-between" }}
                      disableGutters
                      key={item.id}
                      // deleteTodo dispatch 추가
                      onClick={() => {
                        dispatch(deleteTodo(item.id));
                      }}
                    >
                      {item.text}{" "}
                      <DeleteOutlineIcon style={{ float: "right" }} />
                    </ListItem>
                  </CardContent>
                </Card>
              ))}
            </List>

코드의 일부를 발췌하였다. 

ListItem의 onClick() 함수를 생성하고 dispatch를 이용해 deleteTodo에 id값을 넣어준다.

그러면 그 id값이 deleteTodo 라는 이름의 action의 payload로 전달되어 원하는 객체가 삭제될 것이다.

 

 

아래는 할 일 추가 및 삭제 기능을 구현한 화면이다.

휴지통을 누르면 삭제가 되는 것을 볼 수 있다.

할일 추가 및 삭제 기능 구현

 


 

3. hover 및 삭제 커서 디자인

 

너무 밋밋한 느낌이 들어 삭제 아이콘에 마우스를 가져다 대면 손 모양으로 바꾸게 한다.

 

MUI의 icon을 사용한 태그의 style에 cursor : 'pointer' 스타일을 지정해주기만 하면 된다.

<DeleteOutlineIcon style={{ float: "right" , cursor: 'pointer' }} />

cursor: pointer 가 적용된 화면

그러면 위처럼 포인터 형태가 나타나게 된다.

 

hover 추가하기

hover 속성은 마우스가 해당 위치에 올라가게 되면 작동하게 되는 설정이다

개별 할 일들의 카드를 마우스가 올라갈 때 색을 변하도록 구현해 보겠다.

 

App.module.css 에 다음과 같은 속성 값을 추가한다. 클래스명:hover로 사용할 수 있다.

 

.card:hover {
    background-color: aliceblue;
}

 

App.tsx 에 해당 card라는 클래스 스타일을 추가해준다. (전체 코드는 아래에 첨부하겠다.)

 

<Card className={styles.card} style={{ marginTop: "20px" }}>

//....

</Card>

 

 

마우스가 해당할 일 카드에 올라갈 때마다 색이 변하는 것을 볼 수 있다.

 


4. 추가 버튼 눌렀을 때 input 초기화하기

 

지금 현재 상태는 할 일을 입력하고 추가 버튼을 눌러도 input에 텍스트가 그대로 남아있다.

추가 버튼을 누르면 현재 state에 txt를 초기화시켜주고 input의 내용도 없애주는 기능을 구현하도록 하자.

 

해당 버튼에 onClick 함수에 setTxt를 ('') 빈 문자열로 초기화시켜준다.

//App.tsx

	<Button
              style={{ marginTop: "10px", marginLeft: "10px" }}
              variant="outlined"
              onClick={() => {
                dispatch(addTodo(txt));
                setTxt('')
              }}
            >

 

텍스트를 담당하는 TextField에 value에 현재 텍스트인 txt값을 넣어주기만 하면 된다.

	<TextField
              type="text"
              onChange={(e) => setTxt(e.target.value)}
              id="standard-basic"
              label="todo Input"
              variant="standard"
              value={txt}
            ></TextField>

 

추가 버튼을 누르면 input값이 초기화되는 것을 잘 볼 수 있다.

 

 


App.tsx 전체 코드

 

import React, { useState } from "react";
import { AppDispatch, RootState } from "./app/store";
import { useSelector, useDispatch } from "react-redux";

import { addTodo, deleteTodo } from "./features/todos/todoSlice";

//meterial Ui
import CssBaseline from "@mui/material/CssBaseline";
import Container from "@mui/material/Container";
import Box from "@mui/material/Box";

import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";

import List from "@mui/material/List";
import { ListItem } from "@mui/material";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";

import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";

//css modules import
import styles from "./App.module.css";

function App() {
  const todoList = useSelector((state: RootState) => state.todos);
  const dispatch = useDispatch<AppDispatch>();

  // console.log(dispatch);

  let [txt, setTxt] = useState("");

  return (
    <CssBaseline>
      <Container fixed>
        <Box sx={{ height: "100vh" }}>
          <h1 className={styles.center}>Todo List</h1>
          <div className={styles.center}>
            <TextField
              type="text"
              onChange={(e) => setTxt(e.target.value)}
              id="standard-basic"
              label="todo Input"
              variant="standard"
              value={txt}
            ></TextField>
            <Button
              style={{ marginTop: "10px", marginLeft: "10px" }}
              variant="outlined"
              onClick={(e) => {
                dispatch(addTodo(txt));
                setTxt('')
                console.log(e);
              }}
            >
              추가
            </Button>
          </div>
          <div className={styles.center}>
            <h3>할일 내용</h3>

            <List
              dense
              sx={{
                margin: "0 auto",
                width: "100%",
                maxWidth: 360,
                bgcolor: "background.paper",
              }}
            >
              {todoList.map((item) => (
                <Card className={styles.card} style={{ marginTop: "20px" }}>
                  <CardContent>
                    <ListItem
                      style={{ justifyContent: "space-between" }}
                      disableGutters
                      key={item.id}
                      // dispatch 추가
                      onClick={() => {
                        dispatch(deleteTodo(item.id));
                      }}
                    >
                      {item.text}{" "}
                      <DeleteOutlineIcon style={{ float: "right" , cursor: 'pointer' }} />
                    </ListItem>
                  </CardContent>
                </Card>
              ))}
            </List>
          </div>
        </Box>
      </Container>
    </CssBaseline>
  );
}

export default App;

 

todoSlice 전체 코드는 위쪽에서 볼 수있다.

이번 포스팅은 간단한 기능들을 구현해 보았다.

이를 확장시켜서 여러 가지 시도들을 해보아야겠다.

반응형

댓글