App.js:
function App() {
return (
<main>
<AddPostForm />
<PostList />
</main>
);
}
store.js:
export const store = configureStore({
reducer: {
posts: postsReducer
}
})
postSlice.js:
const initialState = [
{ id: 1, title: 'Learning Redux Toolkit', content: 'I have heard good things.' },
{ id: 2, title: 'Slices...', content: 'The more I say slice, the more I want pizza.' }
];
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
postAdded: (state, action) => {
console.log(action.payload)
state.push(action.payload);
}
}
});
export const selectAllPosts = state => state.posts;
export const { postAdded } = postsSlice.actions;
export default postsSlice.reducer;
AddPostForm.js:
function AddPostForm() {
const dispatch = useDispatch();
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
function onSubmit() {
if (title && content) {
console.log('after if');
dispatch(postAdded({
id: nanoid(),
title,
content
}));
setTitle('');
setContent('');
}
}
return (
<section onSubmit={onSubmit}>
<h2>Add New Post</h2>
<form>
<label htmlFor="title">Post Title:</label>
<input
type="text"
id="title"
value={title}
onChange={e => setTitle(e.target.value)}
/>
<label htmlFor="content">Content:</label>
<textarea
id="content"
value={content}
onChange={e => setContent(e.target.value)}
/>
<button>Save Post</button>
</form>
</section>
)
}
export default AddPostForm
PostList.js:
function PostList() {
const posts = useSelector(selectAllPosts);
return (
<section>
<h2>Posts</h2>
{posts.map(post => (
<article key={post.id}>
<h3>{post.title}</h3>
<p>{post.content.substring(0, 100)}</p>
</article>
))}
</section>
)
}
export default PostList
The postAdded
reducer is push
ing to the state, but after I add a test post (via the form), I dont see the form added. However the console.log(action.payload)
does shows the object (for example Object { id: "rTuWGH9pfK-cQvCZecSvt", title: "asd", content: "asd" }
). So why is it not pushing it into the state?
The button
element has type="submit"
by default if not specified. The issue here is that the form
element is submitted and and default form action is not prevented. The result is that the page reloads, e.g. the React app is remounted and all state is the initial state.
Move the onSubmit
handler to the form
element and call preventDefault
on the onSubmit
event object. Also, it's highly advisable to be explicit with attributes, add type="submit"
to the button
element.
function AddPostForm() {
const dispatch = useDispatch();
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
function onSubmit(event) {
event.preventDefault(); // <-- prevent form submission, don't reload page
if (title && content) {
dispatch(postAdded({ title, content }));
setTitle('');
setContent('');
}
}
return (
<section>
<h2>Add New Post</h2>
<form onSubmit={onSubmit}> // <-- onSubmit on form element
<label htmlFor="title">Post Title:</label>
<input
type="text"
id="title"
value={title}
onChange={e => setTitle(e.target.value)}
/>
<label htmlFor="content">Content:</label>
<textarea
id="content"
value={content}
onChange={e => setContent(e.target.value)}
/>
<button type="submit"> // <-- explicit button type
Save Post
</button>
</form>
</section>
);
}
import { createSlice, nanoid } from '@reduxjs/toolkit';
...
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
postAdded: {
reducer: (state, action) => {
state.push(action.payload);
},
prepare: (payload) => ({
payload: {
...payload,
id: nanoid();
},
})
},
},
});