我刚刚开始我的软件开发生涯。我在获取正确的值方面遇到问题。
我有一个组件,我从父组件中获取图像标签作为道具。在子组件中,标签被正确传递。但是每次我尝试在函数中使用 etag 时, this 的值只是第一次渲染页面后获得的初始值。
为什么会发生这种情况以及如何解决此问题?
这是我的代码片段:
<AnnotationCanvas
imagePath={imagePath}
imgEtag={imgEtag}
imageData={imageData}
/>
AnnotationCanvas.js:
import React, { useEffect, useRef, useState, useCallback } from "react";
// Material UI stuff
import BrushOutlinedIcon from "@mui/icons-material/BrushOutlined";
import RectangleOutlinedIcon from "@mui/icons-material/RectangleOutlined";
import AllOutOutlinedIcon from "@mui/icons-material/AllOutOutlined";
import MouseIcon from "@mui/icons-material/Mouse";
import DeleteForeverOutlinedIcon from "@mui/icons-material/DeleteForeverOutlined";
import AdsClickOutlinedIcon from "@mui/icons-material/AdsClickOutlined";
import NoteAddOutlinedIcon from "@mui/icons-material/NoteAddOutlined";
import { HighlightAltOutlined } from "@mui/icons-material";
import {
Paper,
Switch,
Typography,
Button,
Rating,
List,
Tooltip,
ListItemButton,
Divider,
ListItem,
ListItemIcon,
ListItemText,
MenuItem,
IconButton,
Menu,
} from "@mui/material";
import { makeStyles } from "@mui/styles";
import { motion } from "framer-motion";
import paper from "paper";
import { useLocation, useNavigate } from "react-router-dom";
// translation
import { useTranslation } from "react-i18next";
import { createAnnotation, createOrGetImage } from "src/app/api/api";
const useStyles = makeStyles({
crosshairCursor: {
cursor: "crosshair",
},
defaultCursor: {
cursor: "default",
},
annotationList: {
height: "150px",
top: "150px",
},
});
// setting color variables
const transparentBlue = "#6A59FF47";
const transparentRed = "#f7021280";
const AnnotationCanvas = ({ imagePath, imgEtag, imageData }) => {
// global variables & states
const { t } = useTranslation("Annotexray");
const location = useLocation();
const classes = useStyles();
const canvasRef = useRef(null);
const [rect, setRect] = useState(null);
let finalCircle;
let temporareCircle;
// toolbox states
const [circleClicked, setCircleClicked] = useState(false);
const [polygonClicked, setPolygonClicked] = useState(false);
const [cursor, setCursor] = useState(false);
// states to save data
const [imgData, setImgData] = useState();
const [image, setImage] = useState();
const [annotationsState, setAnnotationsState] = useState([]);
const [startPos, setStartPos] = useState({ x: 0, y: 0 });
let getMiddleOfX;
let getMiddleOfY;
// const [circle, setCircle] = useState()
// Switching Cursor mode
const switchCursor = () => {
setCursor((prevState) => !prevState);
};
const getInitialData = async (imgEtag) => {
try {
const path = location.state.content.Key;
const { data } = await createOrGetImage(imgEtag, path);
setImage(data.imageRawData);
setImgData(data.imageDBData);
const img = new Image();
img.src = data.imageRawData;
img.onload = () => {
// eslint-disable-next-line
const width = img.width;
// eslint-disable-next-line
const height = img.height;
console.log(data.imageDBData)
setAnnotationsState(data.imageDBData.annotations);
};
} catch (error) {
console.log("ERROR AT GETINITIALDATA ", error);
}
};
const handleBackToMouse = () => {
setCircleClicked(false);
setCursor(false);
};
const handleCircle = () => {
setCircleClicked((prevState) => !prevState);
};
useEffect(() => {
getInitialData(imgEtag);
}, [imgEtag]);
const createCircle = () => {};
useEffect(() => {
if (image && canvasRef.current) {
const canvas = canvasRef.current;
paper.setup(canvas);
const raster = new paper.Raster(image);
raster.position = paper.view.center;
raster.size = [900, 650];
const layer = paper.project.activeLayer;
layer.addChild(raster);
setRect(canvas.getBoundingClientRect());
}
}, [image, imgEtag]);
// render annotations from BE
useEffect(() => {
if (annotationsState.length > 0) {
// eslint-disable-next-line
annotationsState.map((annotation) => {
// loading Circles from BE
if (annotation.shape === "circle") {
console.log("CIRCLE TRIGGERED");
const xPoint = Number(annotation.center[0]);
const yPoint = Number(annotation.center[1]);
finalCircle = new paper.Path.Circle({
center: new paper.Point(xPoint, yPoint),
radius: annotation.size,
fillColor: transparentBlue,
selected: true,
});
paper.project.activeLayer.addChild(finalCircle);
}
});
}
}, [annotationsState]);
let circle;
let circleRadius;
console.log("IMAGE ETAG", imgEtag)
const handleCircleMouseUp = (event) => {
event.preventDefault();
// temporareCircle.remove();
finalCircle = new paper.Path.Circle({
center: new paper.Point(getMiddleOfX, getMiddleOfY),
radius: circleRadius / 2,
fillColor: transparentRed,
});
paper.view.update();
document
.getElementById("annotationCanvas")
.removeEventListener("mouseup", handleCircleMouseUp);
document
.getElementById("annotationCanvas")
.removeEventListener("mousemove", handleCircleMouseMove);
console.log("IMGETAG ", imgEtag)
try {
console.log("ETAG ",location.state)
const { data } = createOrGetImage(imgEtag, location.state.content.Key).then((res) => res.data);
const updateAnnotationObj = {
imageID: data.imageDBData._id,
annotationObj: {
color: transparentBlue,
borderColor: "green",
center: [getMiddleOfX, getMiddleOfY],
size: circleRadius / 2,
shape: "circle"
}
};
console.log(updateAnnotationObj);
const updatedAnnotation = createAnnotation(updateAnnotationObj).then((res) => res.data)
setAnnotationsState((prevState) => [
...prevState,
updatedAnnotation.data
])
} catch (error) {}
};
const handleCircleMouseMove = (event) => {
event.preventDefault();
getMiddleOfX = (startPos.x + (event.clientX - rect.left)) / 2;
getMiddleOfY = (startPos.y + (event.clientY - rect.top)) / 2;
// calculate Radius
circleRadius = new paper.Point(startPos.x, startPos.y).getDistance(
event.clientX - rect.left,
event.clientY - rect.top
);
// creating temporare circle
circle = new paper.Path.Circle({
center: new paper.Point(getMiddleOfX, getMiddleOfY),
radius: circleRadius / 2,
fillColor: new paper.Color(transparentBlue),
});
if (temporareCircle) {
temporareCircle.remove();
}
temporareCircle = circle;
document
.getElementById("annotationCanvas")
.addEventListener("mouseup", handleCircleMouseUp);
};
const handleCircleMouseDown = ((event) => {
event.preventDefault();
startPos.x = event.clientX - rect.left;
startPos.y = event.clientY - rect.top;
document
.getElementById("annotationCanvas")
.addEventListener("mousemove", handleCircleMouseMove);
});
useEffect(() => {
if (circleClicked && canvasRef.current) {
document
.getElementById("annotationCanvas")
.addEventListener("mousedown", handleCircleMouseDown);
}
return () => {
if (canvasRef.current) {
document
.getElementById("annotationCanvas")
.removeEventListener("mousedown", handleCircleMouseDown);
}
};
}, [circleClicked]);
return (
<motion.div
className="grid grid-cols-3 w-full"
style={{ gridTemplateColumns: "10% 60% 30%" }}
>
{/* START TOOLBOX */}
<motion.div className="grid grid-rows-1 align-center p-24 m-24 justify-center items-center">
<motion.div
className="grid grid-cols-1 items-center justify-center align-center"
style={{ paddingBottom: "30px" }}
>
<Paper
className="shadow rounded-2xl overflow-hidden"
sx={{ width: "55px" }}
>
<List>
<Tooltip title={t("MOUSE")} arrow>
<ListItemButton onClick={handleBackToMouse}>
<MouseIcon />
</ListItemButton>
</Tooltip>
<Divider />
<Tooltip title={t("SWITCHCURSOR")} arrow>
<ListItemButton
style={{
boxShadow: cursor
? "0px 2px 4px -1px rgba(0,0,0,0.2), 0px 4px 5px 0px rgba(0,0,0,0.14), 0px 1px 10px 0px rgba(0,0,0,0.12)"
: "none",
transform: cursor ? "scale(0.98)" : "scale(1)",
color: cursor && "blue",
}}
onClick={switchCursor}
>
<AdsClickOutlinedIcon />
</ListItemButton>
</Tooltip>
<Divider />
<Tooltip title={t("BRUSH")} arrow>
<ListItemButton disabled>
<BrushOutlinedIcon />
</ListItemButton>
</Tooltip>
<Divider />
<Tooltip title={t("BOUNDINGBOX")} arrow>
<ListItemButton
// style={{
// boxShadow: boundingBoxClicked
// ? "0px 2px 4px -1px rgba(0,0,0,0.2), 0px 4px 5px 0px rgba(0,0,0,0.14), 0px 1px 10px 0px rgba(0,0,0,0.12)"
// : "none",
// transform: boundingBoxClicked
// ? "scale(0.98)"
// : "scale(1)",
// color: boundingBoxClicked && "blue",
// }}
disabled
// onClick={() => handleBoundingBox()}
>
<RectangleOutlinedIcon />
</ListItemButton>
</Tooltip>
<Divider />
<Tooltip title={t("POLYGON")} arrow>
<ListItemButton
style={{
boxShadow: polygonClicked
? "0px 2px 4px -1px rgba(0,0,0,0.2), 0px 4px 5px 0px rgba(0,0,0,0.14), 0px 1px 10px 0px rgba(0,0,0,0.12)"
: "none",
transform: polygonClicked ? "scale(0.98)" : "scale(1)",
color: polygonClicked && "blue",
}}
// onClick={() => handlePolygon()}
>
<HighlightAltOutlined />
</ListItemButton>
</Tooltip>
<Divider />
<Tooltip title={t("CIRCLESHAPE")} arrow>
<ListItemButton
style={{
boxShadow: circleClicked
? "0px 2px 4px -1px rgba(0,0,0,0.2), 0px 4px 5px 0px rgba(0,0,0,0.14), 0px 1px 10px 0px rgba(0,0,0,0.12)"
: "none",
transform: circleClicked ? "scale(0.98)" : "scale(1)",
color: circleClicked && "blue",
}}
onClick={() => handleCircle()}
>
<AllOutOutlinedIcon />
</ListItemButton>
</Tooltip>
{/* TODO: Change this functionality to a delete all functionality */}
{/* <Divider />
<Tooltip title={t("DELETE")} arrow>
<ListItemButton
disabled={annotationsState && annotationsState.length === 0}
onClick={handleDeleteLastAnnotation}
>
<DeleteForeverOutlinedIcon />
</ListItemButton>
</Tooltip> */}
</List>
</Paper>
</motion.div>
</motion.div>
{/* END OF TOOLBOX */}
<motion.div className="w-full p-24">
<canvas
ref={canvasRef}
id="annotationCanvas"
className={cursor ? classes.crosshairCursor : classes.defaultCursor}
style={{ width: 900, height: 650 }}
/>
</motion.div>
{/* START OF ANNOTATIONSWIDGET */}
<motion.div>
<Paper
className={`flex flex-col flex-auto p-24 shadow rounded-2x1 overflow-auto ${classes.annotationList}`}
>
<motion.div>
<Typography
className="px-16 text-lg font-medium tracking-tight leading-6 truncate"
color="text.secondary"
>
{t("ANNOTATIONS")}
</Typography>
</motion.div>
<motion.div className="flex flex-col items-center">
<List className="w-5/6">
{annotationsState && annotationsState.length > 0 ? (
annotationsState.map((annotation, index) => {
let annotationIcon;
if (annotation.shape === "polygon") {
annotationIcon = <HighlightAltOutlined />;
} else if (annotation.shape === "circle") {
annotationIcon = <AllOutOutlinedIcon />;
}
return (
<motion.div key={index}>
<ListItem key={index} disablePadding>
<ListItemIcon>{annotationIcon}</ListItemIcon>
<ListItemText
primary={
<Typography
variant="body1"
style={{
fontWeight: "bold",
}}
>
{`${index + 1}.${
annotation.tag === undefined
? "unknown"
: annotation.tag
}`}
</Typography>
}
/>
<ListItemIcon style={{ color: "red" }}>
<ListItemButton
// onClick={() => handleDeleteLastAnnotation(index)}
>
<DeleteForeverOutlinedIcon />
</ListItemButton>
</ListItemIcon>
</ListItem>
{index !== annotationsState.length - 1 && <Divider />}
</motion.div>
);
})
) : (
<motion.div>{t("NOANNOTATIONS")}</motion.div>
)}
</List>
</motion.div>
</Paper>
</motion.div>
{/* END OF ANNOTATIONSWIDGET */}
</motion.div>
);
};
export default AnnotationCanvas;
我在handleCricleMouseUp函数之外得到的值是正确的,但是这个函数中的值是旧值。
希望我能尽快得到你们的帮助,我花了一周时间试图找出为什么会发生这种情况。
我尝试将值保存在状态中,而不是将其作为道具获取 - 不起作用 我尝试将该值作为道具传递,但它只是不以同样的方式工作