redux에서 createAsyncThunk는 무엇입니까?
39690 단어 tutorialreduxreactjavascript
Redux에서 미들웨어는 항상 비동기 작업을 수행하는 데 사용되었습니다. 비동기 작업은 API에서 데이터를 가져오는 것과 같이 기다려야 하는 작업을 의미합니다. 미들웨어는 개발자가 부작용이 있는 논리를 작성할 수 있도록 설계되었습니다. 예를 들어
redux-thunk
라는 패키지가 있습니다.Redux-thunk는 비동기 논리(작업)에 사용됩니다.
Redux 툴킷은 기본적으로
redux-thunk
를 포함하므로 redux-thunk
와 같은 기본 제공 종속성이 제공되므로 createAsyncThunk
를 사용하여 비동기 요청을 할 수 있습니다.createAsyncThunk
CreateAsyncThunk는 슬라이스에서 비동기 작업을 수행하는 곳입니다. 두 개의 매개변수를 받습니다.
"posts/fetchPosts"
createAsyncThunk
를 사용하여 생성된 각 작업에 대해 반환된 약속에 대해 세 가지 가능한 상태가 있습니다. pending
, fulfilled
, rejected
.Redux가 API 호출의 세(3) 단계에서 수행해야 할 작업을 결정합니다. 슬라이스 내부에
extraReducers
, pending
및 fulfilled
API 반환을 처리하는 몇 가지 함수를 포함하는 rejected
라는 속성을 추가합니다.extraReducers
extraReducers를 사용하여
createAsyncThunk
에 의해 생성된 작업을 처리합니다. 약속의 상태에 따라 상태를 업데이트합니다.나는 당신이 redux 툴킷에 대해 조금 알고 있다고 가정하고 설정을 통해 빠르게 실행할 것입니다.
주목
단일 기능에 대한 모든 파일은 동일한 폴더에 있어야 합니다. 즉, 게시물과 관련된 모든 항목은
posts
라는 폴더에 있어야 합니다.가게를 차리다
// src/app/store.js
import { configureStore } from '@reduxjs/toolkit'
import postsReducer from '../features/posts/postsSlice'
export const store = configureStore({
reducer: {
// reducer for slice goes here
},
})
export default store
스토어를 앱에 제공
enitre 앱을 스토어로 래핑합니다.
// index.js
import App from './App';
import { store } from './app/store'
import { Provider } from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
슬라이스 만들기
// src/features/posts/postsSlice
import { createSlice } from '@reduxjs/toolkit'
import { createAsyncThunk } from '@reduxjs/toolkit'
import axios from "axios"
const BASE_URL = "https://jsonplaceholder.typicode.com/posts"
const initialState = {
posts: [],
status: "idle",
error: ""
}
export const fetchPosts = createAsyncThunk("posts/fetchPosts", async () => {
const response = await axios.get(BASE_URL)
console.log(response.data)
return response?.data
})
export const deletePost = createAsyncThunk("post/deletePost", async (initialPost) => {
const {id} = initialPost
try {
const response = await axios.delete(`${BASE_URL}/${id}`);
if (response?.status === 200) return initialPost;
return `${response.status} : ${response.statusText}`;
} catch (error) {
return error.message
}
})
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
// ==> normal reducer functions go here
},
extraReducers(builder) {
builder
.addCase(fetchPosts.pending, (state, action) => {
state.status = "loading"
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = "succeeded"
state.posts = state.posts.concat(action.payload);
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = "failed"
state.error = action.error.message
})
.addCase(deletePost.fulfilled, (state, action) => {
if (!action?.payload.id) {
console.log("could not delete");
console.log(action.payload)
return
}
const { id } = action.payload;
const OldPosts = state.posts.filter(post =>
post.id !== id)
state.posts = OldPosts
})
}
})
export default postsSlice.reducer;
상태에 액세스하기 위해 많은 선택자를 만듭니다.
선택기를 사용하면 상태의 특성이 변경되는 경우 모든 항목을 한 곳에서 업데이트할 수 있습니다.
주목
이 작업은 여전히 게시물 슬라이스 내부에서 수행됩니다.
// src/posts/postsSlice
export const selectAllPosts = (state) => state.posts.posts
export const getPostsError = (state) => state.posts.error
export const getPostsStatus = (state) => state.posts.status
스토어에 슬라이스 리듀서 추가
// src/app/store.js
import { configureStore } from '@reduxjs/toolkit'
import postsReducer from '../features/posts/postsSlice'
export const store = configureStore({
reducer: {
posts: postsReducer
},
})
export default store
앱이 로드되는 즉시 이 게시물 배열을 가져옵니다.
// index.js
import { fetchPosts } from './features/posts/postsSlice';
store.dispatch(fetchPosts());
게시물 구성 요소
// src/features/posts/Posts.jsx
import React from 'react'
import { useSelector } from 'react-redux/es/hooks/useSelector'
import { selectAllPosts, getPostsError, getPostsStatus } from './postsSlice'
import TableData from './TableData'
const Posts = () => {
// selectors to access state
const posts = useSelector(selectAllPosts);
const status = useSelector(getPostsStatus);
const error = useSelector(getPostsError);
let content;
if (status === "loading") {
content = <div className="text-center my-5">Loading...</div>
} else if (status === "succeeded") {
// change the order of the posts
const orderedPosts = posts.slice().sort((a, b) => a - b)
content = orderedPosts.map((post, i) => (
<TableData key={i} post={post} />
))
} else if (status === "failed") {
content = (
<>
<h1>Posts not found</h1>
<p className='text-center text-danger'>{error}</p>
</>
)
}
return (
<section className="section">
<div className="container">
<div className="row">
<div className="col-12 text-center">
<h3>Here are all the posts</h3>
</div>
</div>
<div className="row">
<div className="col-12">
{content}
</div>
</div>
</div>
</section>
)
}
export default Posts
TableData 구성 요소
재사용 가능한 구성 요소를 만들기 위해 관심사 분리를 사용했습니다.
// src/features/posts/TableData.jsx
import React from 'react'
import { deletePost } from './postsSlice'
import { useDispatch } from 'react-redux'
import { useNavigate } from "react-router-dom";
const TableData = ({ post }) => {
const navigate = useNavigate();
const { id } = post;
const dispatch = useDispatch();
const handleDelete = () => {
try {
// dispatch action to store
dispatch(deletePost({ id })).unwrap();
navigate("/")
} catch (error) {
console.log(`Failed to delete the post ${error}`)
}
}
return (
<div className="item">
<div>
<h3>{post.title}</h3>
<p className="postCredit">
{post.body}
</p>
</div>
<div>
<button className="btn btn-danger" onClick={handleDelete}>
delete
</button>
</div>
</div>
)
}
export default TableData
앱 구성 요소
import './App.css';
import { BrowserRouter as Router, Route, Routes } from "react-router-dom"
import Posts from './features/posts/Posts';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Posts />} />
</Routes>
</Router>
);
}
export default App;
CSS
여기 내 CSS가 있습니다.
App.css
또는 index.css
에 넣을 수 있습니다.* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 1rem;
}
body{
min-height: 100vh;
}
.App{
display: flex;
flex-direction: column;
justify-content: flex-start;
min-height: 100vh;
}
.section{
background-color: whitesmoke;
border: 1px solid blue;
flex: 1;
}
/* custom styling */
.item {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
padding: 18px;
background-color: aqua;
border: 2px solid dodgerblue;
margin: 10px 0;
}
.item-text{
font-size: 1.2rem;
margin: 0;
padding: 0;
}
package.json
에 다음 패키지를 추가하고 npm install
를 실행합니다."overrides": {
"autoprefixer": "10.4.5"
},
"@reduxjs/toolkit": "^1.8.3",
"bootstrap": "^5.1.3",
"react-router-dom": "^6.3.0",
"react-redux": "^8.0.2",
감사합니다
github
Reference
이 문제에 관하여(redux에서 createAsyncThunk는 무엇입니까?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/neildegrassetyson/what-is-createasyncthunk-in-redux--mhe텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)