Thunk리덕스에서 비동기 작업을 처리할 때 사용
액션 객체가 아닌 함수를 디스패치 할 수 있다.
npm install redux thunk 가 설치되어 있어야 사용 가능하다.
thunk 사용 예시const store= createStore(rootReducer,applyMiddleware(ReduxThunk, logger));
react에서 비동기를 사용할 수 있게 해주는 redux-thunk
redux-thunk의 구성const thunk= store=> next=> action=> {
typeof action=== 'function'?
action(store.dispatch, store.getState):
next(action)
}
[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 일치하는 항목 찾아서 반환
}
//import { getPosts, getPostById } from '../api/posts.js';
//getPosts()
import * as postsAPI from '../api/posts.js';
//*는 모든 함수를 의미
//*로 받으면 함수들이 들어있는 객체로 받는다.
//as는 받아온 데이터에 이름을 지정해주는 것
import axios from 'axios';
//postAPI.getPosts()
//1. 액션타입
//포스트 여러개 조회하기
const GET_POSTS= "GET_POSTS";
const GET_POSTS_SUCCESS = "GET_POSTS_SUCCESS";
const GET_POSTS_ERROR= "GET_POSTS_ERROR";
//포스트 하나 조회하기
const GET_POST = "GET_POST";
const GET_POST_SUCCESS = "GET_POST_SUCCESS";
const GET_POST_ERROR = "GET_POST_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})
}
}
data 불러오기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;
}
}
import { combineReducers } from "redux";
import counter from "./counter";
import posts from "./posts";
// const rootReducer = combineReducers({ counter: counter })
export const rootReducer = combineReducers({ counter: counter, posts: posts }); //생략가능
export default rootReducer;
export default PostList;
import React from 'react';
import { Link } from 'react-router-dom';
const PostList = ({ posts }) => {
return (
<ul>
{posts.map(post=><Link to={`/${post.id}`} key={post.id}><li>
{post.title}
</li></Link>)}
</ul>
);
};
export default PostList;
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PostList from '../components/PostList';
import { getPosts } from '../modules/posts';
const PostListContainer = () => {
const { data, loading, error } = useSelector(state=>state.posts.posts);
const dispatch = useDispatch();
//컴포넌트 마운트 후 포스트 목록 요청
useEffect(()=>{
dispatch(getPosts())
},[dispatch])
if(loading) return <div>로딩 중 .....</div>;
if(error) return <div>에러 발생</div>;
if(!data) return null;
return (
<PostList posts = {data} />
);
};
export default PostListContainer;
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { applyMiddleware, legacy_createStore as createStore } from 'redux';
import { rootReducer } from './modules';
import { Provider } from 'react-redux';
// import myLogger from './middlewares/myLogger';
import logger from 'redux-logger';
import ReduxThunk from 'redux-thunk';
//스토어 만들기
const store = createStore(rootReducer, applyMiddleware(ReduxThunk, logger));
console.log(store.getState());
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import './App.css';
import CounterContainer from './containers/CounterContainer';
import PostContainer from './containers/PostContainer';
import PostListContainer from './containers/PostListContainer';
function App() {
return (
<BrowserRouter>
<div className="App">
<CounterContainer />
<Routes>
<Route path="/" element={<PostListContainer />} />
<Route path="/:id" element={<PostContainer />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
server 설치npm init 으로 package.json 설치npm install express 로 package-lock.json 설치npm install cors 설치