環境の準備
①ターミナルでreactアプリケーションを作成する。
npx create-react-app <プロジェクト名> --template redux
cd <プロジェクト名>
yarn start
② 必要なパッケージをインストールする。
npm install @reduxjs/toolkit react-redux
コンポーネント・ファイル構成
src
├─ features
└── Posts.js
├── App.css
├── App.js
├── DummyData.js
└── index.js
src / features / Posts.js
import { createSlice } from '@reduxjs/toolkit';
import { PostsData } from '../DummyData';
export const postSlice = createSlice({
name: 'posts',
initialState: { value: PostsData },
reducers: {
addPost: (state, action) => {
state.value.push(action.payload);
},
deletePost: (state, action) => {
state.value = state.value.filter((post) => post.id !== action.payload.id);
},
},
});
export const { addPost, deletePost } = postSlice.actions;
export default postSlice.reducer;
src/App.css
.App {
display: flex;
flex-direction: column;
align-items: center;
padding: 1.5rem;
height: 100vh;
}
.App h1 {
font-size: 2.5rem;
}
hr {
margin-top: 20px;
margin-bottom: 0;
}
.displayPosts .post {
margin-top: 30px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
padding: 20px 30px;
border-radius: 10px;
min-width: 500px;
background-color: white;
}
.displayPosts .postName {
font-size: 25px;
margin-top: 6px;
}
.displayPosts .postContent {
margin-top: -10px;
font-size: 15px;
font-weight: 400;
}
input {
padding: 10px 20px;
margin-right: 10px;
border: none;
box-shadow: 0 1px 3px 0 rgba(123, 123, 123, 0.1),
0 6px 20px 0 rgba(118, 118, 118, 0.19);
outline: none;
}
button {
padding: 7px 30px;
border: none;
border-radius: 4px;
box-shadow: 0 1px 3px 0 rgba(123, 123, 123, 0.1),
0 6px 20px 0 rgba(55, 55, 55, 0.19);
background-color: royalblue;
color: white;
cursor: pointer;
}
src/App.js
import './App.css';
import { useSelector, useDispatch } from 'react-redux';
import { addPost, deletePost } from './features/Posts';
import { useState } from 'react';
function App() {
const [name, setName] = useState('');
const [content, setContent] = useState('');
//useSelectorでstoreの中のstateにアクセスできる。usersはreducer名
const postList = useSelector((state) => state.posts.value);
const dispatch = useDispatch();
const handleClick = () => {
dispatch(
addPost({
id: postList.length + 1,
name: name,
content: content,
})
);
setName('');
setContent('');
};
return (
<div className='App'>
<div>
<h1>React-Redux掲示板</h1>
</div>
<div className='addPost'>
<input
type='text'
placeholder='お名前'
onChange={(e) => setName(e.target.value)}
value={name}
/>
<input
type='text'
placeholder='投稿内容'
onChange={(e) => setContent(e.target.value)}
value={content}
/>
<button onClick={() => handleClick()}>投稿</button>
<hr />
</div>
<div className='displayPosts'>
{postList.map((post) => (
<div key={post.id} className='post'>
<h1 className='postName'>{post.name}</h1>
<h1 className='postContent'>{post.content}</h1>
<button onClick={() => dispatch(deletePost({ id: post.id }))}>
削除
</button>
</div>
))}
</div>
</div>
);
}
export default App;
src / DummyData.js
export const PostsData = [
{
id: 1,
name: 'ShinCode',
content: 'Redux勉強なう',
},
{
id: 2,
name: '名無しさん',
content: 'ShinCodeさん分かりやすい',
},
{
id: 3,
name: '駆け出しエンジニア',
content: 'Udemy受講してみようかな',
},
{
id: 4,
name: 'スタックオーバーフローさん',
content: 'エンジニアのスタバ',
},
];
src / index.css
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: lightyellow;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
src / index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import postsReducer from './features/Posts';
const store = configureStore({
reducer: {
posts: postsReducer,
},
});
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);