React 상태 관리 라이브러리 중에 Redux를 지난번에 사용해 보았다.
다만 Redux는 비동기 상태관리가 안되기 때문에 Redux-saga라는 Middleware를 적용하여 기능을 확장 시켜줘야 한다.
사용법에 대해서 알아보자.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux'; //Provider import
import createSagaMiddleware from 'redux-saga'; // middleware 호출
import { configureStore } from '@reduxjs/toolkit'; //store 생성을 위한 import
import catsReducer from './catState' //reducer 호출
import catSaga from './catSaga' // Saga파일 호출
const saga=createSagaMiddleware(); // middleware를 호출한다.
const store=configureStore({ //store를 생성하고, 만든 Reducer를 가져온다.
reducer:{
cats:catsReducer
},
middleware:[saga]
});
saga.run(catSaga) //Saga를 run 해준다.
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}> //사용할 component에 provider를 감싸고 store를 부여해준다.
<App />
</Provider>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
import { createSlice } from '@reduxjs/toolkit'; //createSlice호출
export const catSlice=createSlice({ // Slice를 만들어준다.
name:'cats', //이름을 저장
initialState:{ //초기값을 저장
cats:[],
isLoading:false,
number:10,
},
reducers:{ //변경해주는 기능인 reducer를 지정해준다.
getCatsFetch:(state)=>{ //getCatsFetch를 호출하면 로딩상태를 true로 바꿔준다.
state.isLoading=true
},
getCatsSuccess:(state,action)=>{ //getCatsSuccess를 호출하면 fetch한 action을 payload로 저장해주고 로딩상태를 false로 바꿔준다.
state.cats=action.payload.slice(0,state.number)
state.isLoading=false;
},
getCatsFailure:(state)=>{ // 실패시 로딩을 false로 변경
state.isLoading=false;
},
getCatsNumber: (state)=>{ //더보기 버튼을 누를 시 볼 갯수를 늘려준다.
state.number=state.number+10
}
}
})
export const {getCatsFetch,getCatsSuccess,getCatsFailure,getCatsNumber} = catSlice.actions; //reducer를 export해준다.
export default catSlice.reducer; //기본 state를 export해준다.
import {call,put,takeEvery} from 'redux-saga/effects'; // 주요 기능인 call, put, takeEvery를 불러온다.
import {getCatsSuccess} from './catState'//reducer를 불러온다.
import catSlice from './catState' //state를 불러온다.
import {getCatsNumber} from './catState' //reducer를 불러온다.
function* workGetCatsFetch(){
const cats=yield call( )=>fetch('https://api.thecatapi.com/v1/breeds'))// 사용할 URL에서 데이타를 fetching 한다.
const formattedCats=yield cats.json(); //결과 파일을 json 형태로 바꾼다.
console.log("result",getCatsNumber())
// const formattedCatsShortened=formattedCats.slice(0,20);
yield put(getCatsSuccess(formattedCats)) //가져온 결과를 getCatsSuccess라는 reducer를 이용하여 action을 저장한다.
}
function* catSaga(){ //catSaga를 통해서 cats내에 getCatsFetch/getCatsNumber를 watching 한다. watching하고 workGetCatsFetch를 실행한다.
yield takeEvery('cats/getCatsFetch',workGetCatsFetch)
yield takeEvery('cats/getCatsNumber',workGetCatsFetch)
}
export default catSaga; //saga 파일을 export 한다.
import './App.css';
import {useEffect,useState} from 'react'
import {useSelector, useDispatch} from 'react-redux' //redux 기본 기능을 가져온다.
import {getCatsFetch} from './catState' //reducer를 가져온다
import {getCatsNumber} from './catState' //reducer를 가져온다
function App() {
const cats=useSelector(state=>state.cats.cats) // state의 값을 가져온다.
const dispatch=useDispatch();
useEffect(() => {
dispatch(getCatsFetch());
}, [dispatch]); //dispatch할 때 데이타를 fetch하도록 reducer를 호출한다.
console.log(cats)
return (
<div className="App">
<h1>CAT SPECIES GALLERY</h1>
<p>Images of different species of cats. Lots of cats for your viewing pleasure.</p>
<hr />
<div className="gallery">
{cats.map(cat =>
<div key={cat.id} className="row">
<div className='column column-left'>
<img
alt={cat.name}
src={cat.image.url}
width="200"
height="200"
/>
</div>
<div className='column column-right'>
<h2>{cat.name}</h2>
<h5>Temperament:{cat.temperament}</h5>
<p>{cat.description}</p>
</div>
</div>
)}
</div>
<button onClick={ ()=>{dispatch(getCatsNumber())} }>VIEW MORE CATS</button> //버튼을 누르면 더 보기를 해주도록 dispatch에 getCatsNumber를 담아 보낸다.
</div>
);
}
export default App;
▶ 주요 내용들을 boiler-plate로 만들어놓을 필요가 있어 보인다.
▶ 다만 fetching 부위만 특수하고 나머지 부분은 redux-toolkit와 동일하니, fetching 부위를 고정하고 redux-toolkit을 유동적으로 바꾸어 사용하면 유용해 보인다.!
▶ Mobx나 Recoil은 좀 더 쉽다고 하니 배워보자!