간단하게 useReducer 사용법을 익힐 겸 todoList를 만들어보기로 했다.
구현한 기능은
일정을 추가하는 기능
추가된 일정을 삭제하는 기능
각 일정들에 줄을 긋는 기능(일정을 다 했다는 표시)
이렇게 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를 적용해보자.
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(
<>
할 일: ??
추가
</>
)
}
이렇게 각각 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(<>
실행
이렇게 실행할 수 있다!!!!!!
전체 코드
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>
</>)
}