我正在编写一个带有任务显示页面的自动化即服务前端(开源)。我已经对其进行了所有常见的调试,并发现问题似乎出在 onChange 处理程序上,主要是从消除过程中得出的,而且还在整个代码中使用了调试语句,而且它甚至没有进入处理程序事件函数。所以,这并不是说 Web 组件被阻止或发生任何事情(除了很可能被其他事件阻止)。
引入错误是因为我在这个页面添加了流程。最初,该页面有一个请求文件上传的按钮。页面上的表格采用 JSON 或 YAML 文件,并将该文件中将发生的任务列出到表格中,以便可以查看和编辑它们。一切都很好。行显示在表格中。
然后,我有了一个绝妙的主意,安装 dJango_eventstream 和 daphne 来启用服务器端事件,这样我就可以显示一个对话框,说明导入文件时发生的情况,而不是让用户耐心等待一分钟(没有用户有耐心)。这破坏了它,但破坏似乎是由于向页面添加多个新事件来处理导入对话框窗口和 SSE 事件。按钮代码和其他所有内容之前都可以正常工作。我重写了事件处理程序,以更简洁的方式组合事物,但没有成功。
我坦白承认,我是这里的Python开发人员。这是我的第一个 React 项目和第一个 JavaScript 项目,所以我不知道我在做什么。我可以想办法解决,因为它与我所知道的相似,但总的来说,javascript 事件远远超出了我的理解范围。我可能对这里的各个概念有所了解,然后在尝试将它们结合起来时完全失败了。
有问题的代码(删除表设置以使其变小):
import React, { useState, useEffect } from 'react';
import { DataGrid } from '@mui/x-data-grid';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import Copyright from '../internals/components/Copyright';
import axios from 'axios';
import ImportDialog from './importDialog';
const API_URL = 'http://backend:8000/';
const callRestApi = (endpoint, method = 'GET', body) => {
const sessionToken = window.sessionStorage.getItem('sessionToken');
const headers = {
'Accept': 'application/json',
};
if (sessionToken) {
headers['Authorization'] = `Token ${sessionToken}`;
} else {
console.warn('No session token found in storage. This request might fail if authorization is required.');
}
return axios({
method,
url: `${API_URL}/${endpoint}`,
data: body,
headers,
}).catch(err => {
console.error('Request failed:', err);
});
};
export default function Tasks() {
const [tasks, setTasks] = useState([]);
const [selectedFile, setSelectedFile] = useState(null);
const [dialogOpen, setDialogOpen] = useState(false);
const [importResults, setImportResults] = useState('');
useEffect(() => {
callRestApi("tasks/", 'GET', null).then((res) => {
if (res && res.data) {
setTasks(res.data);
} else {
console.error("Failed to load tasks data.");
}
});
}, []);
const handleFileChange = (event) => {
alert("Handle File Change");
const file = event.target.files[0];
if (file) {
setSelectedFile(file);
handleFileUpload(file);
}
event.target.value = ''; // reset the file input
};
const handleFileUpload = async (file) => {
if (!file) {
alert("Please select a file first.");
return;
}
const formData = new FormData();
formData.append('file', file);
try {
const uploadResponse = await axios.post(`${API_URL}api/tasks/api_upload/`, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
console.log('File uploaded:', uploadResponse.data);
// Start SSE after upload
const eventSource = new EventSource(`${API_URL}/events/import/`);
const messages = [];
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
messages.push(data.text);
setImportResults(messages.join("\n"));
setDialogOpen(true);
};
eventSource.onerror = (error) => {
console.error('SSE error:', error);
eventSource.close();
};
} catch (error) {
console.error('Error uploading file:', error);
}
};
const handleDialogClose = () => {
setDialogOpen(false);
};
return (
<Box sx={{ width: '100%', maxWidth: { sm: '100%', md: '1700px' } }}>
<Box sx={{ width: '100%', maxWidth: { sm: '100%', md: '1700px' } }}>
<Typography color="blue" component="h2" variant="h6" sx={{ mb: 2 }}>
Tasks
</Typography>
<input
type="file"
onChange={handleFileChange}
accept=".yaml,.json"
id="file-input"
style={{ display: 'none' }}
/>
<Button
variant="contained"
startIcon={<CloudUploadIcon />}
onClick={() => {
document.getElementById('file-input').click();
}}
>
Upload API Definition
</Button>
</Box>
<Box sx={{ width: '100%', maxWidth: { sm: '100%', md: '1700px' } }}>
<DataGrid
autoHeight
checkboxSelection
columns={columns}
rows={tasks}
getRowClassName={(params) =>
params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
}
initialState={{
pagination: { paginationModel: { pageSize: 20 } },
}}
pageSizeOptions={[10, 20, 50]}
density="compact"
/>
<Copyright sx={{ my: 4 }} />
</Box>
{/* Import Dialog */}
<ImportDialog
open={dialogOpen}
handleClose={handleDialogClose}
importResults={importResults}
/>
</Box>
);
}
我尝试手动触发按钮,只有当我将事件函数直接放在按钮的 onClick 事件中时才有效。它不会以正确的方式工作,这也表明事件没有触发。
我尝试将按钮移动到另一个位置,看看主题是否挡住了它,但没有成功。
我尝试取出除按钮定义和事件之外的所有代码以查看问题所在,但由于我保留了事件定义,我认为这就是失败的原因。
然后,在放入更多调试语句并意识到它仅在通过事件调用它时才失败后,我重新编写了事件处理程序并取出了所有 this (this) 引用,但仍然没有运气。
我最终解决了这个纯粹是偶然的事情,这也是最可笑的事情。代码没有任何问题。我使用Mac在docker中进行开发。经过至少 2 到 3 天的努力,我碰巧注意到类似的对话框在另一个不相关的应用程序中不起作用。 Mac 很少出现错误或问题,而且它们不会重新启动,所以我从来没有想到我可能遇到了关闭 Mac 界面上文件对话框的错误。如果我没有偶然注意到这一点,我可能一个月都没有解决这个问题。所以,这是一个事件问题,只是不在我的应用程序中。对话框事件中的一些错误已完全全局关闭 Mac 界面上的文件对话框。简单的重新启动并重新加载 docker 镜像就解决了这个问题。
谢谢。