reactjs - 为什么在更新页面位置时 axios/mongodb findOneAndReplace 调用有效,但每当向对象添加附加字段时返回 undefined?
问题描述
请提供任何可以指向代码的指针,我是 React 的新手。
此处的应用程序在黑板上创建新卡片(“页面”),当将页面拖动到黑板上的任何位置时,对 mongodb 数据库的调用会调用 findOneAndReplace 以使用新坐标替换该页面为新页面状态。这样,应用程序关闭后,页面位置会保留在黑板上。此外,用户可以将小部件拖到每个页面上,这些小部件也保存为页面卡片状态和数据库中的对象数组。
我花了几个晚上,无法调试这个问题。如果我拖动页面、添加页面或删除页面,状态更新正常,数据库调用更新正常。如果我将一个或多个小部件拖到页面上,则状态会使用新的小部件对象数组正常更新,并且它会在应用程序上正常显示。但是,对 mongodb 的 POST 调用返回“未定义”。最奇怪的是,在那之后,如果我将页面拖到任何地方,它就会开始回调 undefined,并且该页面开始表现得很奇怪。不过,任何没有小部件的页面都可以正常工作。
我试图从错误中获得更多,而不仅仅是“未定义”。但是一旦在页面对象中添加了一个小部件字段,post 方法似乎根本不会调用。我尝试在 POST 方法中添加控制台日志,但在添加小部件后,即使在 findOneAndReplace 调用之前,它们也不会在 post 函数中打印。
这是成功传递给 post 方法时页面对象的样子(在任何小部件被拖到页面上之前)
这是添加一些小部件后页面对象的样子,任何对发布和更新页面的调用都使用页面移动时使用的相同发布方法返回“未定义”
更令人担忧的是,当一个小部件被拖到页面上时,页面 x 和 y 坐标会轻微移动......
我真的不确定页面组件中正在更新的小部件状态与“pagetree”黑板上的页面状态之间是否存在一些冲突,或者我是否对 Axios 做错了什么?
如果可以的话请帮忙!如果您需要,很高兴发布完整代码谢谢
pageCard组件(坐在黑板上的组件)上的Widget回调方法和onDragEnd方法
const updateWidgets = useCallback((newWidgets) => {
setWidgets(newWidgets);
}, [widgets]);
function handleOnDragEnd(result) {
if (!result.destination) return;
const items = Array.from(widgets);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
setWidgets(items);
widgetCallback(items,_id);
}
调用 API 的 pageTree(页面黑板)父组件的代码
import React, { useCallback, useEffect, useState } from 'react';
import PageCard from './PageCard';
import { useDrop } from "react-dnd";
import { ItemTypes } from "../Utils/items";
import Api from "../api.js"
const styles = {
width: 1000,
height: 3000,
border: '1px solid black',
position: 'relative',
};
function PageTree({AddNewPageFunc}) {
const [pages, setPages] = useState([]);
const updatePage = useCallback((droppedPage) => {
const updatedPages = pages.map(page => droppedPage._id == page._id ? droppedPage : page);
//console.log("updated pages ",updatedPages);
setPages(updatedPages);
//console.log("set pages ",pages);
}, [pages]);
const [{isOver}, drop] = useDrop(() => ({
accept: ItemTypes.PAGECARD,
drop(page, monitor) {
const delta = monitor.getDifferenceFromInitialOffset();
let x = Math.round(page.x + delta.x);
let y = Math.round(page.y + delta.y);
page.x = x;
page.y = y;
page.widgets = [{name: "widget", thing: "widgetthing", header: "otherWidgetThing"}];
savePage(page, page._id);
updatePage(page);
return undefined;
},
}), [updatePage]);
const savePage = (pageDetails, id) => {
console.log("function called to update page: ",pageDetails);
Api.withToken().post('/pageupdate/'+id,
pageDetails
).then(function (response) {
console.log("?worked ",response.data)
}).catch(function (error) {
console.log("page save failed for some reason: ",error.response);
});
}
React.useEffect(() => {
AddNewPageFunc.current = AddNewPage
}, [pages])
const AddNewPage = useCallback(() => {
Api.withToken().post('/addblankpage/'
).then(function (response) {
console.log("produced: ",response.data);
setPages([...pages,response.data])
}).catch(function (error) {
console.log(error.response);
});
}, [pages]);
const handleDeletedCallback = useCallback((deletedIndex) => {
//I HAVE NO IDEA WHY I HAVE TO CALL SETPAGES TWICE, BUT THAT IS THE RULES, IT WON'T WORK IF YOU DON'T...
setPages(pages.splice(deletedIndex, 1));
setPages(pages);
}, [pages]);
const WidgetAddCallback = (widgets, pageID) => {
console.log("widget callback fired requesting widgets ",widgets," for ID ",pageID)
const newPageIndex = pages.findIndex(page => page._id === pageID);
const updatedPage = Object.assign(pages[newPageIndex], {widgets: widgets});
console.log("updated page is ",updatedPage)
savePage(updatedPage, pageID);
updatePage(updatedPage);
}
useEffect(() => {
Api.withToken().get('/pages/') //can add in a prop to return only a given tree once the app gets bigger
.then(res => {
setPages(res.data);
})
}, []);
return (
<div ref={drop} style={styles}>
{pages.map((page, index) => (<PageCard page={page} id={page._id} key={page._id} index={index} widgetCallback ={WidgetAddCallback} deleteCallback={handleDeletedCallback} handleMaximise={() => handleMaximise(page)} handleCopy={() => handleCopy(page)}/>))}
</div>
)
}
export default PageTree;
使用 axios 的 API 文件
import axios from 'axios';
const baseURL = process.env.REACT_APP_BASE_URL || "http://localhost:3001"
export default {
noToken() {
return axios.create({
baseURL: baseURL
});
},
withToken() {
const tokenStr = window.sessionStorage.getItem("token")
return axios.create({
baseURL: baseURL,
headers: {"Authorization" : `Bearer ${tokenStr}`}
});
}
}
用于更新页面的 POST 方法,在添加小部件时根本不再调用
const express = require('express')
const router =new express.Router()
const Pages = require('../models/page')
const auth = require('../middleware/authentication')
router.post('/pageupdate/:_id',auth, async(req,res)=>{
//console.log("received for pageUpdate API: ",req.body);
const query = { "_id": req.params };
const replacementPage = req.body;
const options = { "returnNewDocument": false };
Pages.findOneAndReplace(query, replacementPage, null, function (err, doc) {
if (err){
console.log(err)
}
else{
console.log("replaced successfully");
}})
})