8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

组件正在无限重新渲染

silverdagger 1月前

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[]);

我不知道如何解决这个问题

帖子版权声明 1、本帖标题:组件正在无限重新渲染
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由silverdagger在本站《reactjs》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 1. 尝试逐个注释 useEffect 并测试 2. 之后尝试重构你的组件(使用、钩子、函数来简化调试)

返回
作者最近主题: