Thunk

hanyoko·2023년 6월 24일

REACT

목록 보기
14/15
post-thumbnail

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)
}

작성하는 방법

1. 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 일치하는 항목 찾아서 반환
}

2. thunk 작성

//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;
	}
}

3. redux combineReducers

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;

4. 로딩이 완료 되었을 때 출력할 구문 작성

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;

5. 로딩상태에 따라 출력될 구문 작성

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;

6. 연결

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 expresspackage-lock.json 설치
  • npm install cors 설치

0개의 댓글