What you'll learn
- How to add slices of reducer logic to the Redux store with
createSlice- Reading Redux data in components with the
useSelectorhook- Dispatching actions in components with the
useDispatchhook
redux-essentials-example-app에서 다운 받아서 npm install 후 npm start 하면
아래와 같은 창이 나온다.

@reduxjs/toolkit and react-redux packagesconfigureStore API, and pass in at least one reducer function.src/index.js)<Provider> component from React-ReduxReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)

/public : HTML host page template and other static files like icons/src/index.js<Provider> component and the main <App> component/src/App.js/src/index.css : Styles for the complete application/src/api/client.js : small AJAX request client that allows us to make GET and POST request/src/api/server.js/src/app/Navbar.js : Renders the top header and nav content/src/app/store.js : creates the Redux store instance/src/features/posts/postsSlice.jsx (SE)Redux Toolkit createSlice function to make a reducer function that knows how to handle our posts data
Reducer functions need to have some initial data included so that the Redux store has those values loaded when the app starts up.
Array with some fake post objects inside, so that we can begin adding the UI.
import { createSlice } from '@reduxjs/toolkit'
// Define initial posts array
const initialState = [
{ id: '1', title: 'First Post!', content: 'Hello!' },
{ id: '2', title: 'Second Post!', content: 'More Text' },
]
const postsSlice = createSlice({ // createSlice generates posts reducer
name: 'posts',
initialState, // Pass initialState to createSlice
reducers: {}
})
// Export the posts reducer function
export default postsSlice.reducer
/src/app/store.jsximport { configureStore } from '@reduxjs/toolkit'
// import `postReducer` function
import postsReducer from '../features/posts/postsSlice'
// Update the call to `configureStore`
// `postReducer` is being passed as a reducer field named `posts`
export default configureStore({
reducer: {
posts: postsReducer
}
})
posts insidestate.posts will be updated by postsReducer function when actions are dispatchedNow we have some posts data in our store
We can create a React component that shows the list of posts
All of the code related to our feed posts feature should go in the posts folder
React components can read data from the Redux store using useSelector hook from the React-Redux library
/src/features/posts/PostsList.jsximport React from 'react'
// React components read data from Redux store
import { useSelector } from 'react-redux'
export const PostsList = () => {
// Read `state.posts` value from Redux store
const posts = useSelector(state => state.posts)
const renderedPosts = posts.map(post => (
<article className="post-excerpt" key={post.id}>
<h3>{post.title}</h3>
<p className="post-content">{post.content.substring(0, 100)}</p>
</article>
))
return (
<section className="posts-list">
<h2>Posts</h2>
{renderedPosts}
</section>
)
}
App.jsPostsList component into App.js and replace the welcome text with `/src/app.jsximport React from 'react'
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
import { Navbar } from './components/Navbar'
import { PostsList } from './features/posts/PostsList'
function App() {
return (
<Router>
<Navbar />
<div className="App">
<Routes>
{/* 기본 경로("/")에 대해 PostsList를 렌더링 */}
<Route path="/" element={<PostsList />} />
{/* 잘못된 경로로 접근할 경우 리디렉션 */}
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</div>
</Router>
)
}
export default App

AddPostForm.jsx in posts foldersrc/features/posts/AddPostForm.jsximport React, { useState } from 'react'
export const AddPostForm = () => {
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
const onTitleChanged = e => setTitle(e.target.value)
const onContentChanged = e => setContent(e.target.value)
return (
<section>
<h2>Add a New Post</h2>
<form>
<label htmlFor="postTitle">Post Title:</label>
<input
type="text"
id="postTitle"
name="postTitle"
value={title}
onChange={onTitleChanged}
/>
<label htmlFor="postContent">Content:</label>
<textarea
id="postContent"
name="postContent"
value={content}
onChange={onContentChanged}
/>
<button type="button">Save Post</button>
</form>
</section>
)
}
src/app.jsximport React from 'react'
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
import { Navbar } from './components/Navbar'
import { AddPostForm } from './features/posts/AddPostForm'
import { PostsList } from './features/posts/PostsList'
function App() {
return (
<Router>
<Navbar />
<div className="App">
<Routes>
{/* 기본 경로("/")에 대해 PostsList를 렌더링 */}
<Route path="/" element={
<>
<AddPostForm />
<PostsList />
</>
} />
{/* 잘못된 경로로 접근할 경우 리디렉션 */}
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</div>
</Router>
)
}
export default App
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
postAdded: {
reducer(state, action) {
state.push(action.payload)
},
prepare(title, content) {
return {
payload: {
id: nanoid(),
title,
content
}
}
}
}
// other reducers here
}
})
features/posts/AddPostForm.js
const onSavePostClicked = () => {
if (title && content) {
dispatch(postAdded(title, content))
setTitle('')
setContent('')
}
}