我正在尝试通过一个项目来学习 redux-toolkit。
我在商店内使用“toEditPostId”变量(初始值为空)来获取我要编辑的帖子的 id,当我将其值设置为 id 字符串时,应用程序工作正常,但是当我单击清除时我的表单组件中的按钮调用“dispatch(setToEditPostId(null))”,然后整个应用程序重新渲染并自动调用“handleSubmit”函数,然后添加一个新的空帖子,这是我不希望发生的.
我在导致问题的片段附近添加了“****************”。
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axiosInstance from "../utils/axiosInstance";
export const fetchMemoryPosts = createAsyncThunk('posts/fetchPosts', async (_, thunkApi)=>{
try {
const response = await axiosInstance.get('/posts');
return response.data.posts;
} catch (error) {
const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
const errorCode = error.response.status;
return thunkApi.rejectWithValue({errorMessage,errorCode});
}
});
export const createMemoryPost = createAsyncThunk('posts/createPosts', async(data, thunkApi)=>{
const tagsArr = data.tags.split(',').map((ele)=> ele.trim());
const preparedData = {...data, tags: tagsArr};
try {
const response = await axiosInstance.post('/posts/create', preparedData);
return response.data.data;
} catch (error) {
const errorMessage = error.response.data.match(/Error: (.*?)<br>/)[1];
const errorCode = error.response.status
return thunkApi.rejectWithValue({errorMessage, errorCode})
}
})
const initialState = {
posts: [],
status: 'idle',
error: null,
toEditPostId: null, //The state variable to track the id of the post to edit*************
}
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: { // changing the value to an id and back to null here***********************
setToEditPostId(state,action){
state.toEditPostId = action.payload;
}
},
extraReducers(builder){
builder
.addCase(fetchMemoryPosts.pending, (state,_)=>{
state.status = 'loading';
})
.addCase(fetchMemoryPosts.fulfilled, (state, action)=>{
state.status = 'success';
state.posts = action.payload;
})
.addCase(fetchMemoryPosts.rejected, (state, action)=>{
state.status = 'failure';
state.error = action.payload;
})
.addCase(createMemoryPost.fulfilled, (state, action)=>{
state.status = 'success';
state.posts = state.posts.concat(action.payload);
})
.addCase(createMemoryPost.rejected, (state, action)=>{
state.status = 'failure';
state.error = action.payload;
})
}
});
export const getAllPostsSelector = (state) => state.posts.posts;
export const getPostsErrorSelector = (state) => state.posts.error;
export const getPostsStatusSelector = (state) => state.posts.status;
export const getToEditPostIdSelector = (state) => state.posts.toEditPostId;
export const { setToEditPostId } = postsSlice.actions;
export default postsSlice.reducer;
下面是Form组件。
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createMemoryPost, setToEditPostId, getToEditPostIdSelector, getAllPostsSelector } from '../../features/postsSlice';
const Form = () => {
const dispatch = useDispatch();
const allPosts = useSelector(getAllPostsSelector);
const postId = useSelector(getToEditPostIdSelector);
let targetPost;
if(postId){
targetPost = allPosts.filter((post) => post._id === postId);
}
console.log("pi->", postId)
console.log("po->", targetPost);
const initialPostDataState = {
creator: '', title: '', message:'', tags:''
}
const [postData, setPostData] = useState(initialPostDataState);
// Below function runs unnecessarily when the dispatch(setToEditPostId(null)) is called*********************************
const handleSubmit =(e)=>{
console.log("it ran");
e.preventDefault()
dispatch(createMemoryPost(postData));
setPostData(initialPostDataState);
}
const handleInput =(e)=>{
setPostData({...postData, [e.target.name]: e.target.value})
}
const clearForm = ()=>{
dispatch(setToEditPostId(null))
setPostData(initialPostDataState);
}
return (
<main className='bg-transparent_bg w-full flex'>
<form className='w-full text-center space-y-3 p-2 box-border'
onSubmit={handleSubmit}
>
<h3
className='font-extrabold '
>
{postId !== null? "" : "Create a memory"}
</h3>
<input
className='input'
type="text"
placeholder='Creator'
name='creator'
value={postData.creator}
onChange={handleInput}
/>
<input
className='input'
type="text"
placeholder='Title'
name='title'
value={postData.title}
onChange={handleInput}
/>
<textarea
className='input'
placeholder='Message'
name="message"
cols="30"
rows="5"
value={postData.message}
onChange={handleInput}
/>
<input
className='input'
type="text"
placeholder='Tags (coma seperated)'
name='tags'
value={postData.tags}
onChange={handleInput}
/>
<div className='flex justify-around py-1 box-border'>
<button
type='submit'
className='bg-blue-400 w-24'
>
Submit
</button>
<button // clear button to call dispatch to set value to null *********************
onClick={()=>dispatch(setToEditPostId(null))}
className='bg-red-500 w-24'
>
Clear
</button>
</div>
</form>
</main>
)
}
export default Form;
显式
指定按钮类型,则
button
元素默认具有type="submit"
。
type
按钮的默认行为。可能的值为:
:该按钮将表单数据提交到服务器。 如果未为关联按钮指定属性,则这是默认值 带有submit
,或者属性为空或无效值。<form>
:该按钮将所有控件重置为其初始值,如reset
。 (这种行为往往会惹恼用户。)<input type="reset">
:该按钮没有默认行为,默认情况下按下时不会执行任何操作。它可以让客户端脚本监听 元素的事件,当事件发生时触发。button
将
toEditPostId
状态“清除”回 null 的第二个按钮也是提交表单。指定“清除”按钮 不是 "submit"
类型按钮。
<button
type='submit'
className='bg-blue-400 w-24'
>
Submit
</button>
<button
type="button" // <-- non-submit type button
onClick={() => dispatch(setToEditPostId(null))}
className='bg-red-500 w-24'
>
Clear
</button>