TypeScript : 투두리스트 만들기 (4단계 - Thunk + Axios )

728x90
반응형

 

TypeScript : 투두리스트 만들기 (3단계 - Json-server)

TypeScript : 투두리스트 만들기 (2단계 - Redux Toolkit) TypeScript : 투두리스트 만들기 (1단계 - React Props) Lv1 React를 이용한 TodoList를 만듭니다. Todo를 Create, Delete, Update(완료/취소) 가 가능해야 합니다. 이

zerotonine2da.tistory.com

Lv4

  • Lv4의 과제에서 데이터베이스의 비동기 처리 로직을 추가합니다.
  • Lv3의 코드에서, createAsyncThunk를 추가하여 json-server 상태 관리 로직을 다루도록 합니다.

Keyword

Thunk


1. redux -thunk 설치

yarn add redux-thunk

1. 조회

[App.tsx]

import { __getTodos } from './redux/modules/todoSlice';
import { useAppDispatch } from './redux/config/configStore';

function App() {
    const dispatch = useAppDispatch();

    //데이터 가져오기
    useEffect(() => {
        dispatch(__getTodos());
    }, [dispatch]);

    return (

1) useEffect로 mount 될때 조회되도록 설정

2) dispatch 사용해서 __getTodos (thunk 사용)

 

[todoSlice.tsx]

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import api from '../../axios/api';

export interface Todo {
    id: string;
    title: string;
    content: string;
    isDone: boolean;
}

interface TodoState {
    todos: Todo[];
    isLoading: boolean;
    isError: boolean;
    Error: string | null;
}

const initialState: TodoState = {
    todos: [],
    isLoading: false,
    isError: false,
    Error: null,
};

//조회
export const __getTodos = createAsyncThunk('getTodos', async (payload, thunkAPI) => {
    try {
        const response = await api.get('/todos');
        return thunkAPI.fulfillWithValue(response.data);
    } catch (error) {
        console.log(error);
    }
});
 

const todoSlice = createSlice({
    name: 'todos',
    initialState,
    reducers: {},
    extraReducers: (builder) => {
        builder
            .addCase(__getTodos.pending, (state, action) => {
                state.isLoading = true;
                state.isError = false;
            })
            .addCase(__getTodos.fulfilled, (state, action) => {
                state.isLoading = false;
                state.todos = action.payload;
            })
            .addCase(__getTodos.rejected, (state, action) => {
                state.isLoading = false;
                state.isError = true;
                state.Error = action.error.message || 'error';
            });
       
    },
});
export default todoSlice.reducer;

1) 비동기 통신하면 시간의 차이가 발생할 수 있음=> isLoading, isError, Error 항목 추가

2) initialState의 타입 설정 : TodoState

__getTodos : 비동기 thunk 액션

* createAsyncThunk : 비동기 작업을 처리하는 액션 생성 부분

* 매개변수 payload : thunk 액션에 전달되는 데이터

* 매개변수 thunkAPI : 여기서 비동기함수 수행함

3) extraReducers 사용해서 각각 pending, fulfilled, rejected 상황에 맞게 넣어줌

 

2. 추가 ★

[todoSlice.tsx]

//추가
export const __addTodos = createAsyncThunk<Todo[], {}>('addTodos', async (payload, thunkAPI) => {
    try {
        const response = await api.post(`/todos/`, payload);
        thunkAPI.dispatch(__getTodos());
        return thunkAPI.fulfillWithValue(response.data);
    } catch (error) {
        console.log('todoSlice [add] error', error);
        throw error;
    }
});

1) createAsyncThunk  다음에 2가지의 제네릭타입 작성

* 첫번째 제니릭 타입 : Todo[ ] : thunk 액션의 비동기 작업 성공하면 들어가는 결과

* 두번째 제니릭 타입 : { } :  추가할 newData 타입 (payload로 전달되는 타입 )

 

[InputForm.tsx]

function InputForm() {
 
    const dispatch = useAppDispatch();

    
    const formSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
       
       
     const newData = {
            id: uuid(),
            title,
            content,
            isDone: false,
        };

        dispatch(__addTodos(newData));

1) dispatch 타입 :  store 설정하는 부분에서 설정해줌

export type AppDispatch = typeof store.dispatch;
export const useAppDispatch: () => AppDispatch = useDispatch;

 

2) newData 타입 : todoSlice.tsx 파일에서 두번째 제니릭 타입 (객체])

즉,  추가할 newData 타입 (payload로 전달되는 타입 ) ==> 배열 안에 객체여서서 객로 들어가야함

 

3. 삭제

[todoSlice.tsx]

export interface Todo {
    id: string;
    title: string;
    content: string;
    isDone: boolean;
}

interface TodoState {
    todos: Todo[];
    isLoading: boolean;
    isError: boolean;
    Error: string | null;
}

const initialState: TodoState = {
    todos: [],
    isLoading: false,
    isError: false,
    Error: null,
};
 
export const __deleteTodos = createAsyncThunk<Todo[], string>('deleteTodos', async (payload, thunkAPI) => {
    try {
        const response = await api.delete(`/todos/${payload}`);
        thunkAPI.dispatch(__getTodos());
        return thunkAPI.fulfillWithValue(response.data);
    } catch (error) {
        console.log('todoSlice [delete] error', error);
        throw error;
    }
});
 
extraReducers: (builder) => {
        builder .addCase(__deleteTodos.pending, (state, action) => {
                state.isLoading = true;
                state.isError = false;
            })

1) 알아두기 : extraReducers에서 state값은 슬라이스리듀서를 생성할 떄 정의한 "initialState'에 정의한 타입

=> state 타입 = TodoState임

=> 나는 state의 타입은 TodoState의 todos를 사용하고싶음

==> useSelector함수를 사용할때 todos를 선택하면 됨

 const todos = useSelector((state: RootState) => state.todos.todos);

 

 

[Content.tsx]

    const removeHandler = async (e: React.MouseEvent<HTMLButtonElement>, id: string) => {
        try {
            dispatch(__deleteTodos(id));
        } catch (error) {
            console.log('삭제 오류', error);
        }
    };

1) 이벤트의 타입과 아이디의 타입 지정해주기

2) dispatch로 삭제할 아이디 전달

 

4. 변경

[todoSlice.tsx]

export const __changeTodos = createAsyncThunk<Todo, { id: string; isDone: boolean }>(
    'changeTodos',
    async (payload, thunkAPI) => {
        try {
            const response = await api.patch(`/todos/${payload.id}`, { isDone: !payload.isDone });
            thunkAPI.dispatch(__getTodos());

            return thunkAPI.fulfillWithValue(response.data);
        } catch (error) {
            console.log('todoSlice [delete] error', error);
            throw error;
        }
    }
);

1) 전달 받은 아이디 & isDone 상태값 변

 

 

[Content.tsx]

    const changeHandler = async (e: React.MouseEvent<HTMLButtonElement>, id: string, isDone: boolean) => {
        try {
            dispatch(__changeTodos({ id, isDone }));
        } catch (error) {
            console.log('상태 업데이트 오류', error);
        }
    };

1) 이벤트의 타입과 아이디 & isDone 상태  타입 지정해주기

2) dispatch로 삭제할 아이디 & isDone  전달

 

 

끝.

반응형