React报错:Actions Must be Plain Objects 问题求助
问题原因与解决方案
核心错误:setSelectedProject 动作创建函数不符合Redux Thunk规范
你的「Actions Must be Plain Objects」错误根源在于ProjectActions.js中的setSelectedProject函数写法错误。Redux Thunk要求异步动作创建函数返回一个接收dispatch和getState的函数,而不是直接将dispatch作为参数传入。当前写法会导致Redux尝试将这个异步函数当作普通动作对象处理,从而抛出错误。
修复步骤:
1. 修正setSelectedProject动作创建函数
修改ProjectActions.js中的setSelectedProject:
// 错误写法 export async function setSelectedProject(dispatch, getState) { const response = await axios.get('http://localhost:8000/api/projects/'); dispatch({ type: SET_SELECTED_PROJECT, payload: response.data, }); } // 正确写法(同步动作,用于存储选中的项目) export const setSelectedProject = (project) => ({ type: SET_SELECTED_PROJECT, payload: project, }); // 保留已符合规范的异步详情获取函数 export const fetchProject = (projectId) => async (dispatch) => { try { const res = await axios.get(`http://localhost:8000/api/projects/${projectId}`); dispatch({ type: SET_SELECTED_PROJECT, payload: res.data, }); } catch (err) { console.error(err); } };
说明:如果只是要将选中的项目数据存入Redux,直接返回纯对象动作即可;如果需要从API拉取详情,使用fetchProject这个符合Thunk规范的函数。
2. 消除组件中Redux状态与本地状态的冲突
ProjectList.js中同时维护了Redux状态和本地状态(如selectedProject、showDetails),这会导致数据不一致。改为完全使用Redux状态:
const ProjectList = ({ projects: reduxProjects, showDetails, selectedProject, addProject, removeProject, setSelectedProject, setShowDetails }) => { const dispatch = useDispatch(); const [showPopup, setShowPopup] = useState(false); const [isPopupClosed, setIsPopupClosed] = useState(false); const [projects, setProjects] = useState([]); // 从API获取项目列表 useEffect(() => { axios.get('http://127.0.0.1:8000/api/projects/') .then((res) => { setProjects(res.data); }) .catch((err) => { console.log(err); }); }, [isPopupClosed]); const handleEditClick = (project) => { console.log('Edit button clicked', project); // 将选中项目存入Redux dispatch(setSelectedProject(project)); // 触发侧边弹窗显示 dispatch(setShowDetails(true)); }; const handleCloseDetails = () => { dispatch(setShowDetails(false)); dispatch(setSelectedProject(null)); }; // ...其余代码保持不变,注意替换本地selectedProject和showDetails为props中的Redux状态 };
同时更新映射配置:
const mapStateToProps = (state) => ({ projects: state.project.projects, showDetails: state.project.showDetails, selectedProject: state.project.selectedProject, }); const mapDispatchToProps = { addProject, removeProject, setSelectedProject, setShowDetails, };
3. 优化弹窗关闭后的列表刷新逻辑
避免使用window.location.href = '/'硬刷新页面,改为重新请求API更新列表:
const closePopup = () => { setShowPopup(false); // 重新获取项目列表 axios.get('http://127.0.0.1:8000/api/projects/') .then(res => setProjects(res.data)) .catch(err => console.log(err)); };
进阶方案:新增fetchProjects动作存入Redux,统一管理项目列表状态。
4. 确保ProjectDetails组件正确更新状态
在ProjectDetails.js中,更新项目后同步刷新列表:
const handleUpdateProject = (event) => { event.preventDefault(); updateProject(formInputs).then(() => { // 更新后重新获取项目列表 axios.get('http://127.0.0.1:8000/api/projects/') .then(res => { // 若使用Redux则dispatch更新动作,否则通知父组件刷新 }); setSelectedProject(null); setIsEditing(false); }); };
关键注意事项
- Redux Thunk异步动作必须是返回函数的函数,普通同步动作返回纯对象即可。
- 尽量避免组件本地状态与Redux状态重复,保持单一数据源。
- 页面刷新优先通过API请求更新数据,而非硬刷新页面。
内容的提问来源于stack exchange,提问作者Lewis Northcott
相关产品推荐
相关产品推荐

