반응형
React Redux Toolkit Posts로 연습하기
./api/interface.ts
export interface PostsListProps {
posts: [
{
id: string,
title: string,
content: string
}
]
}
./app/store.ts
counter내용은 이전에 만든 내용입니다.
// 툴킷에서 구성 저장소를 가져옵니다.
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/count/counterSlice";
import postsReducer from "../features/posts/postsSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
posts: postsReducer
}
});
./features/posts/postsSlice.ts
import { createSlice } from "@reduxjs/toolkit";
import { PostsListProps } from "../../api/interface";
const initialState = [
{
id: "1",
title: "제목입니다.",
content: "Hello World 내용입니다~",
},
{
id: "2",
title: "Slice TEST !",
content: "Slice TEST ing~~",
},
];
const postsSlice = createSlice({
name: "posts",
initialState,
reducers: {
postAdded(state, action) {
state.push(action.payload);
}
}
});
export const seletAllPosts = (state: PostsListProps) => state.posts;
export const { postAdded } = postsSlice.actions;
export default postsSlice.reducer;
./features/posts/PostsList.tsx
import styles from "./PostsList.module.scss";
import { useSelector } from "react-redux";
// import { PostsListProps } from "../../api/interface";
import { seletAllPosts } from "./postsSlice";
import { AddPostForm } from "./AddPostForm";
export const PostsList = () => {
// 아래처럼 사용도 가능합니다.
// const posts = useSelector((state: PostsListProps) => state.posts);
// postsSlice.ts에서 내용을 처리해준 방법입니다.
const posts = useSelector(seletAllPosts);
const renderedPosts = posts.map(post => (
<section key={post.id} className={styles.posts_wrap}>
<h3>{post.title}</h3>
<p>{post.content.substring(0, 100)}</p>
</section>
))
return (
<article className={styles.posts_list_wrap}>
<h2>Posts</h2>
<AddPostForm />
<div className={styles.posts_list}>
{renderedPosts}
</div>
</article>
);
};
./features/posts/AddPostForm.tsx
import { ChangeEvent, useState } from "react";
import { useDispatch } from "react-redux";
// nanoid = 임의의 ID를 생성합니다. uuid같은 느낌입니다.
import { nanoid } from "@reduxjs/toolkit";
import { postAdded } from "./postsSlice";
export const AddPostForm = () => {
const dispatch = useDispatch();
const [title, setTitle] = useState<string>("");
const [content, setContent] = useState<string>("");
const onTitleChanged = (e: ChangeEvent<HTMLInputElement>) => setTitle(e.target.value);
const onContentChanged = (e: ChangeEvent<HTMLTextAreaElement>) => setContent(e.target.value);
const onSavePostClicked = () => {
if (title.length > 1 && content.length > 5) {
dispatch(postAdded({
id: nanoid(),
title,
content
}))
setTitle("");
setContent("");
}
}
return (
<section>
<h2>새로운 포스트 생성하기</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" onClick={onSavePostClicked}>Save Post</button>
</form>
</section>
);
};
./App.tsx
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { Counter } from './features/count/Counter';
import { PostsList } from './features/posts/PostsList';
import { Main } from './pages/Main';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Main />} />
<Route path="/count" element={<Counter />} />
<Route path="/posts-list" element={<PostsList />} />
</Routes>
</Router>
);
}
export default App;
./index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
// redux
import { store } from './app/store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<Provider store={store}>
<App />
</Provider>
);
PostsList.tsx에서 해야 하는 일들을 postsSlice.ts로 옮긴 버전입니다.
./features/posts/postsSlice.ts
import { createSlice, nanoid, PayloadAction } from "@reduxjs/toolkit";
import { PostsListProps, PostsProps } from "../../api/interface";
const initialState = [
{
id: "1",
title: "제목입니다.",
content: "Hello World 내용입니다~",
},
{
id: "2",
title: "Slice TEST !",
content: "Slice TEST ing~~",
},
];
const postsSlice = createSlice({
name: "posts",
initialState,
reducers: {
postAdded: {
reducer(state, action: PayloadAction<PostsProps>) {
state.push(action.payload);
},
prepare(title: string, content: string) {
return {
payload: {
id: nanoid(),
title,
content
}
}
}
}
}
});
export const seletAllPosts = (state: PostsListProps) => state.posts;
export const { postAdded } = postsSlice.actions;
export default postsSlice.reducer;
reducer(state, action: PayloadAction<PostsProps>), prepare(title: string, content: string) 이 두 부분에서 조금 막혔었습니다.
prepare의 반환 값과 action의은 동일한 유형을 가져야 하는데, reducer에 대한 입력 값은 추론할 수 없기 때문에 수동으로 지정해야 했었는데 자꾸 이상한 방향으로 갔던 것 같습니다..ㅠ
./features/posts/AddPostForm.tsx
import { ChangeEvent, useState } from "react";
import { useDispatch } from "react-redux";
// nanoid = 임의의 ID를 생성합니다. uuid같은 느낌입니다.
// import { nanoid } from "@reduxjs/toolkit";
import { postAdded } from "./postsSlice";
export const AddPostForm = () => {
const dispatch = useDispatch();
const [title, setTitle] = useState<string>("");
const [content, setContent] = useState<string>("");
const onTitleChanged = (e: ChangeEvent<HTMLInputElement>) => setTitle(e.target.value);
const onContentChanged = (e: ChangeEvent<HTMLTextAreaElement>) => setContent(e.target.value);
const onSavePostClicked = () => {
if (title.length > 1 && content.length > 5) {
dispatch(postAdded(title, content))
setTitle("");
setContent("");
}
}
return (
<section>
<h2>새로운 포스트 생성하기</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" onClick={onSavePostClicked}>Save Post</button>
</form>
</section>
);
};
dispatch() 부분이 깔끔하게 변한것을 확인할 수 있습니다.
반응형
'React > React' 카테고리의 다른 글
[ React ] 객체 배열 State값 변경하기 (0) | 2023.03.08 |
---|---|
[ React ] Redux Toolkit 연습하기 ( 3 ) (0) | 2023.01.30 |
[ React ] Redux Toolkit 연습하기 ( 1 ) (0) | 2023.01.26 |
[ React ] React CRUD ( Feat. Typescript ) (0) | 2023.01.11 |
[ React ] React Google 연동하기 (0) | 2023.01.02 |