data fetching을 위하여 흔히 사용되는 redux middleware 이다. 이 미들웨어를 사용하여 액션 객체가 아닌 함수를 dispatch할 수 있다.
thunk는 프로그래밍 용어로서는 "미뤄진 작업을 처리하는 코드 조각"을 뜻한다. 함수를 작성하여 코드가 일정 시간 후에 실행되도록 만들 수 있다. Redux에 국한하여 뜻을 정의하자면, 이는 Redux내의 dispatch, getState와 소통할 수 있는 함수를 작성하는 방법을 말한다.
우선 thunk를 사용하기 위해선 redux-thunk 미들웨어를 redux store에 추가해야한다.
✋ Thunk function 작성법
두 가지 매개변수를 받는다 => Redux store의 dispatch와 getState 메소드
Thunk function은 직접적으로 불리지 않고 dispatch를 통해서 호출된다.
상단의 shop을 누르면 해당 페이지가 뜬다. 본래 코드는 누르자마자 데이터들이 바로 뜨도록 구현되지만, redux-thunk를 사용함으로써 product data들을 가져오는 것을 잠시 딜레이하고 그 시간 동안 로딩 아이콘이 돌아가도록 할 것이다.
export const CATEGOREIS_ACTION_TYPES = {
//SET_CATEGORIES: 'category/SET_CATEGORIES',
FETCH_CATEGORIES_START: 'category/FETCH_CATEGORIES_START',
FETCH_CATEGORIES_SUCCESS: 'category/FETCH_CATEGORIES_SUCCESS',
FETCH_CATEGORIES_FAILED: 'category/FETCH_CATEGORIES_FAILED',
}
import { CATEGOREIS_ACTION_TYPES } from './category.types'
export const CATEGORIES_INITIAL_STATE = {
categories: [],
isLoading: false,
error: null,
};
export const categoriesReducer = (
state = CATEGORIES_INITIAL_STATE,
action = {}
) => {
const { type, payload } = action;
switch (type) {
case CATEGOREIS_ACTION_TYPES.FETCH_CATEGORIES_START:
return { ...state, isLoading: true };
case CATEGOREIS_ACTION_TYPES.FETCH_CATEGORIES_SUCCESS:
return { ...state, categories: payload, isLoading: false };
case CATEGOREIS_ACTION_TYPES.FETCH_CATEGORIES_FAILED:
return { ...state, error: payload, isLoading: false };
default:
return state;
}
};
INITIAL_STATE에는 원래 category 배열만이 있었지만, isLoading과 error가 추가되었다. isLoading의 값으로 로딩 아이콘의 렌더링을 제어할 것이다.export const fetchCategoriesStart = () =>
createAction(CATEGOREIS_ACTION_TYPES.FETCH_CATEGORIES_START);
export const fetchCategoriesSucess = (categoriesArray) =>
createAction(
CATEGOREIS_ACTION_TYPES.FETCH_CATEGORIES_SUCCESS,
categoriesArray
);
export const fetchCategoriesFailed = (error) =>
createAction(CATEGOREIS_ACTION_TYPES.FETCH_CATEGORIES_FAILED, error);
//async func
export const fetchCategoriesAsync = () => {
return async (dispatch) => {
dispatch(fetchCategoriesStart());
try {
const categoriesArray = await getCategoriesAndDocuments("categories");
dispatch(fetchCategoriesSucess(categoriesArray));
} catch (error) {
dispatch(fetchCategoriesFailed(error));
}
};
};
fetchCategoriesAsync 함수가 thunk function이다.fetchCategoriesStart를 dispatch하는 action function을 반환한다. categoriesArray를 불러오는 함수를 비동기적으로 처리한다. 데이터를 받은 후, fetchCategoriesSucess 를 dispatch한다.fetchCategoriesFailed 함수를 부른다.const Shop = () => {
const dispatch = useDispatch();
useEffect(()=>{
dispatch(fetchCategoriesAsync());
},[])
return (
<Routes>
<Route index element={<CategoriesPreview />} />
<Route path=":category" element={<Category />} />
</Routes>
);
};
export default Shop;
fetchCategoriesAsync를 dispatch한다. fetchCategoriesStart 를 dispatch하기 때문에 isLoading 상태값이 true로 바뀐다.const CategoriesPreview = () => {
const categoriesMap = useSelector(selectCategoriesMap);
const isLoading = useSelector(selectCategoriesIsLoading);
return (
<div className="categories-preview-container">
{isLoading ? (
<Spinner />
) : (
Object.keys(categoriesMap).map((title) => {
const products = categoriesMap[title];
return (
<CategoryPreview key={title} title={title} products={products} />
);
})
)}
</div>
);
};
export default CategoriesPreview;
isLoading이 true인 동안은 스피너 컴포넌트가 렌더링되고, categoriesArray가 완성이 되어 isLoading이 다시 false로 바뀌면 제품 컴포넌트들이 보여진다.
잘 읽었습니다. 좋은 정보 감사드립니다.