You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 05:42:03