npm install redux thunk
이 설치되어있어야한다.
const store= createStore(rootReducer,applyMiddleware(ReduxThunk, logger));
react에서 비동기를 사용할 수 있게 해주는 redux-thunk
const thunk= store=> next=> action=> {
typeof action=== 'function'?
action(store.dispatch, store.getState):
next(action)
}
에서 posts의 data에 접근한다면,
useSelector(state=>state.posts.posts.data);
[api/posts.js]
const sleep= n=> new Promise(resolve=>setTimeout(resolve,n));
const posts= [
{id:1, title:"study redux", desc:"go"},
{id:2, title:"study redux middleware", desc:"go!"},
{id:3, title:"study redux-thunk", desc:"go!!"},
]
// post 목록을 return하는 비동기 함수
export const getPosts= async ()=>{
await sleep(500);
return posts;
}
// ID로 post를 리턴하는 비동기 함수
export const getPostById= async (id)=>{
await sleep(500);
return posts.find(post=> post.id===id); // id 일치하는 항목 찾아서 반환
}
[modules/posts.js]
// *로 받으면 함수들이 들어있는 객체로 받음
// as는 받아온 데이터에 이름을 지정해주는것
// postsAPI= {getPosts,getPostByID}
import * as postsAPI from '../api/posts';
// ---액션타입---
const GET_POSTS= "GET_POSTS";
const GET_POSTS_SUCCESS = "GET_POSTS_SUCCESS";
const GET_POSTS_ERROR= "GET_POSTS_ERROR";
// ---💡 thunk 함수---
export const getPosts = ()=> async (dispatch)=>{
dispatch({type: GET_POSTS}) // 요청이 시작됨
try{
const post= await postsAPI.getPosts();
dispatch({type:GET_POSTS_SUCCESS, data:post})
}
catch(e){
dispatch({type:GET_POSTS_ERROR, error:e})
}
}
또는...
npm install axios
가 설치되어있어야한다.
//상 동
// ---💡 thunk 함수---
export const getPosts = ()=>async(dispatch)=>{
dispatch({type: GET_POSTS}) // 요청이 시작됨
try{
const post= await axios.get('http://localhost:3005/posts');
dispatch({type:GET_POSTS_SUCCESS, data:post.data})
}
catch(e){
dispatch({type:GET_POSTS_ERROR, error:e})
}
}
// ---상태 초기값---
const initialState={
posts:{loading: false, data:null, error: null},
}
// ---reducer---
export default function posts(state=initialState, action){
switch (action.type) {
case GET_POSTS:
return{
...state,
posts:{loading: true, data: null, error:null}
};
case GET_POSTS_SUCCESS:
return{
...state,
posts:{loading: false, data: action.data, error:null}
// action.data의 data는 getPosts의 dispatch에서 받아오는 값의 key이다.
};
case GET_POSTS_ERROR:
return{
...state,
posts:{loading: false, data: null, error:action.error}
// action.error의 error는 getPosts의 dispatch에서 받아오는 값의 key이다.
};
default:
return state;
}
}
[modules/index.js]
import { combineReducers } from "redux";
import counter from "./counter";
import posts from "./posts";
const rootReducer= combineReducers({counter, posts});
export default rootReducer;
[components/PostList.js]
import React from 'react';
const PostList = ({posts}) => {
return (
<ul>
{posts.map(post=><li key={post.id}>
{post.title}
</li>)}
</ul>
);
};
export default PostList;
[containers/PostListContainers.js]
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PostList from '../components/PostList';
import { getPosts } from '../modules/posts';
const PostListContainers = () => {
const {data,loading,error}= useSelector(state=>state.posts.posts)
const dispatch= useDispatch();
// 컴포넌트 마운트 후 post 목록을 요청
useEffect(()=>{
dispatch(getPosts())
},[dispatch]);
if(loading) return <div>로딩중</div>
if(error) return <div>에러 발생</div>
if(!data) return null;
return (
<PostList posts={data}/>
);
};
export default PostListContainers;
[/index.js]
❗ import { applyMiddleware, createStore } from 'redux';
❗ import ReduxThunk from 'redux-thunk';
❗ const store = createStore(textReducer,applyMiddleware(ReduxThunk));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
[/App.js]
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import PostListContainers from './containers/PostListContainers';
function App() {
return (
<BrowserRouter>
<div className="App">
<Routes>
<Route path='/' element={<PostListContainers/>}/>
</Routes>
</div>
</BrowserRouter>
);
}
export default App;