useReducer로 Todolist만들기

쏘뽀끼·2024년 7월 20일

react

목록 보기
1/25

간단하게 useReducer 사용법을 익힐 겸 todoList를 만들어보기로 했다.



구현한 기능은

  1. 일정을 추가하는 기능

  2. 추가된 일정을 삭제하는 기능

  3. 각 일정들에 줄을 긋는 기능(일정을 다 했다는 표시)



이렇게 3가지이다.




import { useState } from "react";


export default function UseRenderList(){
 const [list,setList]= useState('');
return(
<>
<h1> TodoList </h1>
<p> 할 일: ?? </p>

<input
 type="text"
 placeholder = "해야 할 일을 작성해주세요."
 value={list}
 onChange={(e)=> setList(e.target.value)}
 />
 
 <button> 추가 </button>

</>

)
}


일단 이렇게 시작을 해 보자.

위 코드에 useReducer를 적용해보자.



``` import { useReducer, useState } from "react";//1. useReducer import하기 //일정 데이터의 타입 지정 interfact List{ id:number;//각 일정에 id 생성 list:string; //각 일정 isHere:boolean;//실행여부판단 }

interface AppState{
count: number;
lists: List[];
}

type AppAction =
| { type: 'add-list'; payload: {list: string } }
| { type: 'delete-list'; payload: { id: number } }
| { type: 'mark-list'; payload: { id: number } }

//3. 컴포넌트 밖에 render함수 만들기
const reducer=(state:AppState, action:Appaction):AppState =>{
switch (action.type) {
case 'add-list'://일정 추가 시
const list = action.payload.list;
const newList = {
id: Date.now(),
list,//변수이름하고 키 이름하고 똑같으니 생략 가능 원래는 name:name
isHere: false,
}
return {
count: state.count+1,//기존 state + 1개씩 더한다. (리스트 1개가 늘어가면 1이 증가)
lists: [...state.lists, newList],//기존 리스트 + 새로운 리스트
};

case 'delete-list'://일정 삭제시
return{
count: state.count-1,// 여긴 반대로 삭제가 되는 것
lists: state.lists.filter((list:List) => list.id !== action.payload.id),
//filter로 list의 id와 삭제하려는 list의 아이디를 비교하여 걸러내기
}

case 'mark-student'://일정체크시 
  return {
    ...state,
    count:state.count,
    lists: state.lists.map((list) =>
      list.id === action.payload.id//id가 일치하면 
        ? { ...list, isHere: !list.isHere }//isHere부분이 바뀌게 하기(true <-> false)
        : list//아니라면 그냥 유지
    ),
  };

default:
  return state;

}
};

const initialState={
count:0,
lists:[],
}

export default function UseRenderList(){
const [list,setList]=useState('');
const [listsInfo, dispatch] = useReducer(render, initialState);//2.state를 생성산다.
//초기값을 initialState로 지정 & listsInfo state를 생성

return(
<>

TodoList

할 일: ??

추가

</>

)
}

 

이렇게 각각 dispatch의 action으로 type을 지정해주면서 요구 사항을 지정할 수 있다. 

 
 <br/>
  <br/>
 

return(

<body >
<h1>TodoList</h1>

<p>할 일 : {listsInfo.count}</p>//카운트를 넘겨줘서 총 리스트 개수를 나오게 한다. 
<div className="addlist">
<input
type="text"
placeholder="해야 할 일을 작성해주세요."
value={list}
onChange={(e)=> setList(e.target.value)}
/>
<button onClick={()=> dispatch({type: 'add-list',payload: {list}})}>추가</button>
//추가 버튼 클릭시 add-list(할일 추가) 타입으로 lisst객체가 생성된다. 
</div>
{listsInfo.lists.map((list:List) =>{//리스트 목록을 map으로 반복해서 
  return(<Todos //Todos 컴포넌트에 넘겨준다. 
    key={list.id} 
    list={list.list} 
    dispatch={dispatch} 
    id={list.id}
    isHere={list.isHere}
    />)
})}
</body>

)

 <br/>
  <br/>
 

이렇게 리턴문에 추가적으로 하위 컴포넌트에 데이터를 넘겨준다.

  <br/>
  <br/>

 

Todos 컴포넌트를 하나 추가하여 

interface TodosProp {
list: string;
dispatch: (action: any) => void;
id: number;
isHere:boolean;
}
export default function Todos({list,dispatch, id,isHere}:TodosProp){
return(<>

{ dispatch({type: "mark-list", payload: {id}})//mark-list타입으로 전달 id데이터 생성해서 앞서 만든 reducer실행 }} >{list} { dispatch({ type: 'delete-list', payload: { id } }) }}>삭제 //delete-list 타입일때 id 데이터를 생성해서 앞서 만든 reducer 실행
) } ```

실행



이렇게 실행할 수 있다!!!!!!

전체 코드

import { useReducer, useState } from "react"
import Todos from "../../components/useReducerList/Todos";
import "./useRenderList.module.scss";
interface List{
  id:number;
  list: string;
  isHere: boolean;//실행여부
}
interface AppState {
  count: number;
  lists: List[];
}
type AppAction =
  | { type: 'add-list'; payload: {list: string } }
  | { type: 'delete-list'; payload: { id: number } }
  | { type: 'mark-list'; payload: { id: number } }

const reducer = (state:AppState, action:AppAction):AppState => {
  switch (action.type) {
    case 'add-list':
      const list = action.payload.list;
      const newList = {
        id: Date.now(),
       list,//변수이름하고 키 이름하고 똑같으니 생략 가능 원래는 name:name
       isHere: false,
      }
      return {
       count: state.count+1,
       lists: [...state.lists, newList],
      };

  case 'delete-list':
    return{
      count: state.count-1,
      lists: state.lists.filter((list:List) => list.id !== action.payload.id),
    }

    case 'mark-list':
      return {
        ...state,
        count:state.count,
        lists: state.lists.map((list) =>
          list.id === action.payload.id
            ? { ...list, isHere: !list.isHere }
            : list
        ),
      };

    default:
      return state;
  }
};
const initialState={
  count: 0,
  lists:[],
}
export default function UseRenderList(){
 const [list,setList]=useState('');
 const [listsInfo, dispatch] = useReducer(reducer,initialState);
 
return(
    <body >
    <h1>TodoList</h1>
    
    <p>할 일 : {listsInfo.count}</p>
    <div className="addlist">
    <input
    type="text"
    placeholder="해야 할 일을 작성해주세요."
    value={list}
    onChange={(e)=> setList(e.target.value)}
    />
    <button onClick={()=> dispatch({type: 'add-list',payload: {list}})}>추가</button>
    </div>
    {listsInfo.lists.map((list:List) =>{
      return(<Todos
        key={list.id} 
        list={list.list} 
        dispatch={dispatch} 
        id={list.id}
        isHere={list.isHere}
        />)
    })}
    </body>
  )
}
interface TodosProp {
  list: string;
  dispatch: (action: any) => void;
  id: number;
  isHere:boolean;
}
export default function Todos({list,dispatch, id,isHere}:TodosProp){
  return(<>
  <div>
  <span
  style={{
    textDecoration : isHere ? 'line-through' : 'none',
    color: isHere ? 'gray' : 'black',
  }}
  onClick={()=>{
    dispatch({type: "mark-list", payload: {id}})
  }}
  >{list}</span> 
  <button onClick={() => { dispatch({ type: 'delete-list', payload: { id } }) }}>삭제</button>
  </div>
  </>)
}

0개의 댓글