[Main Project] UDog / 구현하기 - 상태 변경 업데이트 하기

soohyunee·2023년 3월 12일
0

[Main Project] UDog

목록 보기
5/18

1. 구현하기

진행 상황

  • 상태 업데이트 리덕스 작성

진행 예정

  • 1차 배포, 서버와 연결

2. TIL

리덕스 툴킷으로 상태 변경 업데이트

createAsyncThunk 사용하기

컨펌 모달의 삭제 버튼을 누르면 삭제가 되는 것까지는 구현을 했지만 새로고침을 해줘야지 변경된 상태가 업데이트 됐다. 이전에는 set함수를 사용해서 상태를 변경해줬는데 컨펌 모달을 구현하고 로직이 많이 바뀐 상태라 어떤 식으로 상태 변경을 새로고침이 아닌 방법으로 업데이트 시켜야할 지 감이 안잡혔다.
검색해보니 set함수를 사용해서 상태를 업데이트 하거나 리덕스를 활용해서 업데이트 하는 두가지의 방법이 있었다.

시도한 방법

처음으로 시도한 방법은 삭제 커스텀 훅에 useState를 사용해서 then 이후에 set함수를 실행시켜 상태를 변경해보려고 했다. 하지만 과정이 더욱 복잡해져서 이 방법은 비효율적이라는 생각이 들었다.
그래서 두번째 방법으로는 axios get 요청 커스텀 훅으로 받아온 데이터를 상태로 담아 관리하고자 리스트 리듀서를 만들었다. 리스트 리듀서에 initialState로 요청 커스텀 훅을 실행시킨 데이터를 할당해주었다. 하지만 이 역시도 에러가 났었다.

해결 방법

구글링을 해보니 createAsyncThunk를 사용하면 내가 원하는 결과를 얻을 수 있을 것 같았다. createAsyncThunk는 비동기 처리를 위해 사용하는 미들웨어이다.

const asyncUpFetch = createAsyncThunk('dogSlice/asyncUpFetch', async () => {
	const response = await API.get('/mydog');
	return response.data;
});

createAsyncThunk는 thunk action creator를 반환하는데 extraReducer로 pending, payload, rejected 액션들을 처리할 로직을 직접 작성해야한다.

	extraReducers: (builder) => {
		builder
			.addCase(asyncUpFetch.pending, (state, action) => {
				state.status = 'loading';
			})
			.addCase(asyncUpFetch.fulfilled, (state, action) => {
				state.value = action.payload;
				state.status = 'succeeded';
			})
			.addCase(asyncUpFetch.rejected, (state, action) => {
				state.status = 'failed';
			});
	},

이때 immer 관련 에러가 뜨는데 확인해보니 두가지 부분에서 immer에러가 뜨는 것 같았다. 첫번째로 immer는 배열의 인덱스나 length 속성만 변경할 수 있기 때문에, state.value와 같이 객체 속성에 직접 접근하는 것은 허용되지 않는다. 따라서 initialState를 객체로 변경해야한다.

const initialState = {value: [], status: 'idle'};

export const selectDog = (state) => state.dog.value;

두번째로 Redux Toolkit이 내부적으로 Immer를 사용하여 불변성을 관리하기 때문에 Immer 에러가 발생한다. 따라서 reducers에 전달되는 state 객체는 불변해야 한다. 이를 해결하려면 deleteDog reducer에서 state.value.filter를 직접 호출하지 말고, Immer의 produce 함수를 사용하여 state 객체를 불변하게 유지하도록 해야 한다.
produce 함수는 Immer에서 제공하는 함수로서, 함수 내에서 state를 변경하면 이전 상태(state)의 복사본을 만들어 변경한다.
immer는 우선 npm install immer로 설치해준 뒤 import 해준다. produce 함수를 사용 할 때에는 첫번째 파라미터에는 수정하고 싶은 상태, 두번째 파라미터에는 어떻게 업데이트하고 싶을지 정의하는 함수를 넣어준다. 두번째 파라미터에 넣는 함수에서는 불변성에 대해서 신경쓰지 않고 그냥 업데이트 해주면 다 알아서 해준다.

import produce from 'immer';

reducers: {
		deleteDog: (state, action) => {
			return produce(state, (draftState) => {
				draftState.value = draftState.value.filter((item) => item.id !== action.payload);
			});
		},
	},

그리고 삭제 리듀서에 삭제할 id값을 관리할 액션을 만들고, 카드 컴포넌트의 삭제 버튼을 클릭하면 id값을 업데이트하고 모달 컴포넌트의 삭제하기 버튼을 클릭하면 리스트 리듀서의 id값을 매개변수로 deleteDog를 실행시키면 삭제하면 상태가 변경된다.

	const handleOpenConfirmModal = () => {
		...
		dispatch(deleteId(dog.id));
	};

	const onClickDelete = () => {
      	...
		dispatch(deleteDog(id));
	};

강아지 리스트 컴포넌트에서 axios get 요청 커스텀 훅으로 받아온 데이터를 렌더링 하고 있는데 그 부분을 asyncUpFetch를 디스패치로 실행 시키고, 상태를 useSelector로 꺼내와서 렌더링 시켜주면 된다. 이때 asyncUpFetch를 디스패치로 실행되는 부분을 useEffect로 의존성 배열에는 dispatch 넣어줘서 dispatch가 실행될 때마다 렌더링이 이루어지게 해주면 된다.

	const dogs = useSelector(selectDog);

	useEffect(() => {
		dispatch(asyncUpFetch());
	}, [dispatch]);
profile
FrontEnd Developer

0개의 댓글