组件正在无限重新渲染
7
0
const Survey:React.FC = () => { const dispatch = useDispatch(); const navigation = useNavigate(); const [currentStep, setCurrentStep] = useState(0); const [stepsArray, setStepsArray] = us...
const Survey: React.FC = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const [currentStep, setCurrentStep] = useState(0);
const [stepsArray, setStepsArray] = useState<JSX.Element[]>([]);
const [capturedPicture, setCapturePicture] = useState<string>('');
const [showPopup, setShowPopup] = useState(false);
const [popup, setPopup] = useState(false)
const [loading, setLoading] = useState(false)
const [enableGeolocation, setEnableGeolocation] = useState(false)
const [locationFetched, setLocationFetched] = useState(false)
const [isOnline, setIsOnline] = useState(navigator.onLine);
const currentDate = getCurrentTimeStampOfEndMarket()
const [selectDate, setselectDate] = useState(currentDate)
const employeeDetails = useSelector((state: RootState) => state.jwtToken.jwtTokenData?.UserData);
const imageCaptureLimit = 8;
const newOutlet = useSelector((state: RootState) => state.newOutletSurvey.data);
const surveyJson: SurveyDetail[] = newOutlet ? JSON.parse(newOutlet?.content!) : [];
const memoizedSurveyJson = useMemo(() => surveyJson, [surveyJson]);
const capturedImages = useSelector((state: RootState) =>
state?.photo.data.filter(image => image.source !== 'Survey') ?? []
);
const [surveyResponses, setSurveyResponses] = useState<LocalSurveyResponse[]>([]);
const [validationErrors, setValidationErrors] = useState<{ key: string; errorMessage: string; type: string; }[]>([]);
useEffect(() => {
return () => {
dispatch(clearImage())
setCapturePicture('');
}
}, []);
useEffect(() => {
if (capturedImages.length > 0) {
setCapturePicture(capturedImages[capturedImages.length - 1].images);
}
}, [capturedImages]);
useEffect(() => {
if (memoizedSurveyJson) {
setSteps(memoizedSurveyJson);
}
}, [memoizedSurveyJson]);
const handleCapture = (imgSrc: string) => {
const base64Image = imgSrc.replace(/^data:image\/(png|jpeg);base64,/, '');
setCapturePicture(base64Image);
const data: PhotoModel = {
source: "newOutletSurvey",
images: base64Image
};
dispatch(addPhoto(data));
setShowPopup(false); // Close the popup after capturing
};
const handleClick = () => {
setShowPopup(true);
};
const closePopup = () => {
setShowPopup(false);
};
const onRemoveClick = (index: number) => {
dispatch(deleteImage(index));
};
const updateSurveyResponse = (questionId: string, response: LocalSurveyResponse) => {
setSurveyResponses(prevState => {
const existingIndex = prevState.findIndex(item => item.questionId === questionId);
if (existingIndex !== -1) {
return prevState.map((item, i) => (i === existingIndex ? response : item));
} else {
return [...prevState, response];
}
});
};
const handleSelectedDate = (date: { day: string; month: string; year: string }) => {
const formattedDate = `${date.year}-${date.month}-${date.day}`;
setselectDate(formattedDate);
}
const setSteps = (surveyData: SurveyDetail[]) => {
const updatedSteps = surveyData.map((data, index) => {
const questionId = `${data.type}_${index}`;
switch (data.type) {
case 'text':
return (
<div key={questionId}>
<TextBox
label={data.Label}
placeholder={data.Placeholder}
onPageChange={(text) => {
const newResponse: LocalSurveyResponse = {
questionId,
question: data.Label,
response: [text],
type: data.type,
};
updateSurveyResponse(questionId, newResponse);
}}
/>
</div>
);
case 'checkbox':
return (
<div key={questionId}>
<Label Label={data.Label}>
<div className="flex flex-wrap gap-2 overflow-y-scroll no-scrollbar">
{data.options && data.options.map((option, optionIndex) => (
<div key={optionIndex}>
<ButtonWithText
isEnabled={surveyResponses.find(r => r.questionId === questionId)?.response.includes(option)}
// isSelected={surveyResponses.find(r => r.questionId === questionId)?.response.includes(option)}
label={option}
onClick={() => {
const currentResponse = surveyResponses.find(r => r.questionId === questionId)?.response || [];
const newResponse: LocalSurveyResponse = {
questionId,
question: data.Label,
response: currentResponse.includes(option)
? currentResponse.filter(item => item !== option)
: [...currentResponse, option],
type: data.type,
};
updateSurveyResponse(questionId, newResponse);
}}
type={'mini'}
/>
</div>
))}
</div>
</Label>
</div>
);
case 'capture':
return (
<div className="w-full " key={questionId}>
<Label Label={t("Upload")}>
{capturedImages.length === 0 && (
<div
className="font-poppins-bold rounded-lg flex items-center justify-center w-full h-24"
onClick={() => handleClick()}
style={{ backgroundImage: "url(\"data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='black' stroke-width='2' stroke-dasharray='10' stroke-dashoffset='10' stroke-linecap='butt'/%3e%3c/svg%3e\")" }}
>
<Icons.CAMERA_PLUS size={32} />
</div>
)}
<div className="w-full flex flex-wrap justify-space gap-3">
{capturedImages.length > 0 && capturedImages.length < imageCaptureLimit && (
<div
className="font-poppins-bold rounded-lg flex items-center justify-center bg-white w-24 h-24"
onClick={() => handleClick()}
>
<Icons.CAMERA_PLUS size={32} />
</div>
)}
{capturedImages.map((img: PhotoModel, i: number) => {
const newResponse: LocalSurveyResponse = {
questionId,
question: data.Label,
response: capturedImages.map(img => img.images),
type: data.type,
};
updateSurveyResponse(questionId, newResponse);
return (
<ButtonWithImage
onRemoveClick={() => onRemoveClick(i)}
imageUrl={`data:image//(png|jpeg|jpg);base64,${img.images}`}
alt={`Image ${i}`}
enableCloseButton={true}
key={i}
/>
)
})}
</div>
</Label>
</div>
)
case 'date':
return (
<div key={questionId}>
<Label Label={data.Label}>
<CalendarPicker
dateFormat="dd-mm-yyyy"
initialValue={currentDate}
onDateChange={(date) => {
handleSelectedDate(date);
const formattedDate = `${date.year}-${date.month}-${date.day}`;
const newResponse: LocalSurveyResponse = {
questionId,
question: data.Label,
response: [formattedDate],
type: data.type,
};
updateSurveyResponse(questionId, newResponse);
}}
/>
</Label>
</div>
);
); default:
return null;
}
});
setStepsArray(updatedSteps.filter((step) => step !== null) as JSX.Element[]);
};
const handleNextClick = async () => {
if (!newOutlet) {
navigate('/home');
} else {
const currentSurveyItem = surveyJson[currentStep];
const questionId = `${currentSurveyItem.type}_${currentStep}`;
const currentResponse = surveyResponses.find(r => r.questionId === questionId);
let isValid = true;
let errorMessage = '';
switch (currentSurveyItem.type) {
case 'text':
if (!currentResponse?.response[0]?.trim()) {
isValid = false;
errorMessage = 'The field should not be empty.';
}
break;
case 'checkbox':
if (!currentResponse?.response.length) {
isValid = false;
errorMessage = 'Please select at least one option.';
}
break;
case 'capture':
if (!capturedImages.length) {
isValid = false;
errorMessage = 'Please capture/upload a picture.';
}
break;
case 'date':
if (!currentResponse?.response[0]) {
isValid = false;
errorMessage = 'Please select a date.';
}
break;
}
if (!isValid) {
setValidationErrors(prevErrors => [
...prevErrors.filter(e => e.key !== questionId),
{ key: questionId, errorMessage, type: currentSurveyItem.type }
]);
return;
}
setValidationErrors(prevErrors => prevErrors.filter(e => e.key !== questionId));
if (currentStep < stepsArray.length - 1) {
setCurrentStep(currentStep + 1);
dispatch(clearImage())
setCapturePicture('');
} else {
const submissionResponses: GeneratedSurveyResponse[] = surveyResponses.map(({ question, response, type }) => ({
question,
response,
type
}));
const requestData: NewOutletSurveyResponseCreateRequest = {
surveyId: newOutlet?.id,
surveyData: submissionResponses,
employeeEmail: employeeDetails?.Email,
timeStamp: getCurrentTimeStampOfEndMarket(),
noOfQuestions: memoizedSurveyJson.length,
noOfAnswered: surveyResponses.length,
};
isOnline ? dispatch(createNewOutlet(requestData)) : dispatch(setNewOutLetSurvey(requestData))
dispatch(clearImage());
setCapturePicture('');
dispatch(addRoute(RouterName.SucessfullPage));
navigate(RouterName.SucessfullPage);
}
}
};
const handlePreviousClick = () => {
if (currentStep > 0) {
const currentQuestionId = `${surveyJson[currentStep].type}_${currentStep}`;
setValidationErrors(prevErrors => prevErrors.filter(e => e.key !== currentQuestionId));
setCurrentStep(currentStep - 1);
}
};
return (
<div className="relative h-screen">
{!showPopup && <NavigateHeader />}
{showPopup ? (
<CameraPopup onCapture={handleCapture} onClose={closePopup} />
) : (
<>
<Body>
{!newOutlet ? (
<div className="flex flex-col items-center justify-center h-full">
<Icons.WARNING size={100} />
<h1 className="text-2xl font-bold text-heading items-center mx-5">No survey data available.!</h1>
</div>
) : (
<>
<div>{stepsArray[currentStep]}</div>
{validationErrors.length > 0 && (
<div className="validation-errors">
{validationErrors.map((error, index) => (
<span key={index} className={`${GetStatusColor('pending')}`}>
{error.errorMessage}
</span>
))}
</div>
)}
</>
)}
</Body>
<div className="absolute bottom-4 right-4">
<div className="flex justify-between items-center gap-3">
{newOutlet && currentStep > 0 && (
<ButtonWithIcon
alt="Previous"
color={currentStep === 0 ? 'bg-grey' : 'bg-Ash'}
height_and_width="h-20 w-20"
icon={icons.LEFT_ARROW}
size={36}
onClick={handlePreviousClick}
/>
)}
<ButtonWithIcon
alt="Next"
color="bg-Ash"
height_and_width="h-20 w-20"
icon={icons.RIGHT_ARROW}
size={36}
onClick={handleNextClick}
/>
</div>
</div>
</>
)}
{enableGeolocation && (
<WarningPopUp
message={"Enable Geolocation"}
showPopup={popup}
onClose={() => {
setPopup(false)
setEnableGeolocation(false)
}}
/>
)}
</div>
);
};
export default Survey;
setStepsArray(updatedSteps.filter((step) => step !== null) as JSX.Element[]); setSteps 中的 setSteps 导致组件无限重新渲染。
我该如何解决这个问题
const setSteps = (surveyData: SurveyDetail[]) => {}函数正在无限更新 setStepsArray(updatedSteps.filter((step) => step !== null) as JSX.Element[]);
我不知道如何解决这个问题
收藏的用户(0)
X
正在加载信息~