React 中是否存在与 onClick 函数无法识别相关的 bug?

问题描述 投票:0回答:1

我正在使用 NextJs 和 TypeScript 开发一个项目,并且 onClick 函数出现奇怪的行为。看来它在我的组件中没有被识别。

"use client";
import styles from "./UploadCard.module.css";
import Image from "next/image";
import Card from "@/components/Card/Card";
import { FaCloudUploadAlt, FaFileUpload } from "react-icons/fa";
import { SiMicrosoftexcel } from "react-icons/si";
import { ChangeEvent, FormEvent, useEffect, useState } from "react";
import { useUploadContext } from "@/contexts/uploadContext";
import { useUserContext } from "@/contexts/userContext";
import { useRouter } from "next/navigation";
import { MdOutlineClose } from "react-icons/md";
import { useToastContext } from "@/contexts/toastContext";

import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io";
import SelectAddress from "./SelectAddress";
import Carousel from "./Carousel";
import CarouselSlide from "./CarouselSlide";
import { BASE_IMAGE_PATH } from "@/utils/constants";
import uploadService from "@/services/uploadService";
import { SavedAddress } from "@/services/userService";
import useFetch from "@/hooks/useFetch";
import SpreadSheetOptions from "./SpreadSheetOptions";
import checkIfDuplicated from "@/utils/checkIfDuplicated";
import { isError } from "@/utils/error";
import { isAxiosError } from "axios";

// The use cannot send more than FILE_LIMIT files
const KYLOBYTE = 1000;
const MEGABYTE = KYLOBYTE \* 1000;
const FILE_SIZE_LIMIT = 5 \* MEGABYTE;

function formatFileSize(size: number): string {
if (size \< KYLOBYTE) {
return `${size} B`;
} else if (size \>= KYLOBYTE && size \< MEGABYTE) {
return `${size / KYLOBYTE} KB`;
} else {
return `${size / MEGABYTE} MB`;
}
}

export interface FormData {
street: string;
number: string;
city: string;
state: string;
neighborhood?: string;
zipCode?: string;
}

function UploadCard() {
const \[file, setFile\] = useState\<File | null\>(null);
const \[roundTrip, setCyclicalRoute\] = useState\<boolean\>(false);
const { user } = useUserContext();
const { errorToast, promiseToast } = useToastContext();
const { isUploading } = useUploadContext();
const \[activeItem, setActiveItem\] = useState(0);
const \[headers, setHeaders\] = useState\<string\[\]\>(\[\]);
const { fetchExcelData } = useFetch();
const \[showDropdown, setShowDropdown\] = useState(false);
const \[activeInput, setActiveInput\] = useState("");
const \[selectedAddress, setSelectedAddress\] = useState\<SavedAddress | null\>(
null
);
const \[formData, setFormData\] = useState({
street: "",
number: "",
city: "",
state: "",
neighborhood: "",
zipCode: "",
} as FormData);

const router = useRouter();

const totalTabs = 2;

const disableUpload =
!file ||
!formData.street ||
!formData.number ||
!formData.city ||
!formData.state ||
isUploading;

function handleSelectFile(e: ChangeEvent\<HTMLInputElement\>) {
const files = e.target.files;

    if (!files || files.length != 1) return;
    
    if ((files.item(0)?.size as number) > FILE_SIZE_LIMIT) {
      alert("Você só pode enviar arquivos de até 5 MB");
      return;
    }
    
    setFile(files.item(0));
    fetchExcelData(files.item(0), setHeaders);

}

useEffect(() =\> {
if (user?.points === 0) {
router.push("/credits");
}
}, \[user\]);

const clearInput = () =\> {
if (isUploading) {
errorToast(
"Não é possível enviar outro arquivo enquanto um está sendo enviado"
);
}
setFile(null);
};

const toggleCyclicalRoute = (e: ChangeEvent\<HTMLInputElement\>) =\> {
const checked = e.target.checked;
setCyclicalRoute(checked);
};

const handleSubmit = async (e: FormEvent) =\> {
e.preventDefault();

    if (disableUpload) return;
    
    if (checkIfDuplicated(formData)) {
      errorToast("Colunas duplicadas!");
      return;
    }
    
    promiseToast(
      "Realizando o upload...",
      async () => {
        try {
          await uploadService.uploadDynamicColumns(
            file,
            formData.street,
            formData.number,
            formData.city,
            formData.state,
            roundTrip,
            formData.neighborhood,
            formData.zipCode
          );
          router.push("/history");
        } catch (error) {
          console.log(error);
          if (isAxiosError(error)) {
            throw new Error(error.response?.data.error);
          }
    
          if (isError(error)) {
            throw new Error(error.message);
          }
    
          throw new Error("Erro ao tentar enviar o formulário");
        }
      },
      "Upload realizado com sucesso"
    );

};

const handleInputChange = (e: ChangeEvent\<HTMLInputElement\>): void =\> {
let { name, value } = e.target;

    setFormData({
      ...formData,
      [name]: value,
    });

};

const handleInputClick = (inputName: string) =\> {
setActiveInput(inputName);
setShowDropdown(true);
};

const handleInputBlur = () =\> {
setShowDropdown(false);
};

const moveToNext = () =\> {
setActiveItem((prev) =\> (prev + 1) % totalTabs);
};

const moveToPrevious = () =\> {
setActiveItem((prev) =\> (prev - 1 + totalTabs) % totalTabs);
};

const clearSelectedInput = () =\> {
setSelectedAddress(null);
};

return (
\<Card className={styles\["upload-card"\]}\>
\<Image
src={`${BASE_IMAGE_PATH}/geoportal-logo.png`}
alt="geoportal_logo"
width={200}
height={50}
/\>
\<form onSubmit={handleSubmit}\>
\<Carousel activeItem={activeItem} setActiveItem={setActiveItem}\>
\<CarouselSlide position={0}\>
\<div className={styles\["container"\]}\>
\<div className={styles\["card-title"\]}\>
<h1>Roteirização</h1>
\<IoIosArrowForward
className={styles\["tab-icon"\]}
onClick={() =\> {
moveToNext();
}}
/\>
\</div\>
<p>Selecione uma tabela com endereços (Excel)</p>
\<div\>
\<label htmlFor="upload" className={styles\["label"\]}\>
\<span\>Selecionar arquivo\</span\>
\<FaFileUpload className={styles\["upload-icon"\]} /\>
\</label\>
\</div\>
\<input
                onChange={handleSelectFile}
                type="file"
                name="upload"
                id="upload"
                accept=".xls, .xlsx"
              /\>
\<div className={styles\["uploaded-file"\]}\>
{file ? (
\<div\>
\<div\>
\<SiMicrosoftexcel className={styles\["upload-icon"\]} /\>
\<span\>
{file.name} - {formatFileSize(file.size)}
\</span\>
\</div\>
\<MdOutlineClose
className={styles\["button-icon"\]}
onClick={clearInput}
/\>
\</div\>
) : (
\<span\>Nenhum arquivo selecionado\</span\>
)}
\</div\>
\<SelectAddress setSelectedAddress={setSelectedAddress} /\>
\<div className={styles\["uploaded-address"\]}\>
{selectedAddress ? (
\<div className={styles\["uploaded-address-container"\]}\>
\<span\>
{selectedAddress?.name}, {selectedAddress?.nearNumber} -{" "}
{selectedAddress?.city}, {selectedAddress?.state}
\</span\>
\<MdOutlineClose
className={styles\["button-icon"\]}
onClick={() =\> clearSelectedInput()}
/\>
\</div\>
) : (
\<span\>Nenhum endereço selecionado\</span\>
)}
\</div\>
\<button
className={styles\["select-fields"\]}
onClick={() =\> moveToNext()}
type="button"
\\>
Selecione os campos da planilha
\</button\>
\</div\>
\</CarouselSlide\>
\<CarouselSlide position={1}\>
\<div className={`${styles["container"]} ${styles["second"]}`}\>
\<div className={styles\["card-title"\]}\>
<h1>Campos da planilha</h1>
\<IoIosArrowBack
className={styles\["tab-icon"\]}
onClick={moveToPrevious}
/\>
\</div\>
\<div className={styles\["form-content"\]}\>
\<div\>
\<label htmlFor="street"\>Rua (1° Coluna)\</label\>
\<input
id="street"
type="text"
required
onChange={handleInputChange}
onClick={() =\> handleInputClick("street")}
onBlur={handleInputBlur}
name="street"
value={formData.street}
placeholder="Escreva o nome da sua coluna"
/\>
\<SpreadSheetOptions
headers={headers}
disabled={!showDropdown || activeInput != "street"}
inputName="street"
setFormData={setFormData}
/\>
\</div\>
\<label htmlFor="number"\>Número (2° Coluna)\</label\>
\<input
id="number"
type="text"
required
onChange={handleInputChange}
onClick={() =\> handleInputClick("number")}
onBlur={handleInputBlur}
value={formData.number}
name="number"
placeholder="Escreva o nome da sua coluna"
/\>
\<SpreadSheetOptions
headers={headers}
disabled={!showDropdown || activeInput != "number"}
inputName="number"
setFormData={setFormData}
/\>
\<div\>
\<label htmlFor="city"\>Cidade (3° Coluna)\</label\>
\<input
id="city"
type="text"
required
onChange={handleInputChange}
onClick={() =\> handleInputClick("city")}
onBlur={handleInputBlur}
name="city"
value={formData.city}
placeholder="Escreva o nome da sua coluna"
/\>
\<SpreadSheetOptions
headers={headers}
disabled={!showDropdown || activeInput != "city"}
inputName="city"
setFormData={setFormData}
/\>
\</div\>
\<div\>
\<label htmlFor="state"\>Estado (4° Coluna)\</label\>
\<input
type="text"
required
onChange={handleInputChange}
onClick={() =\> handleInputClick("state")}
onBlur={handleInputBlur}
name="state"
value={formData.state}
placeholder="Escreva o nome da sua coluna"
/\>
\<SpreadSheetOptions
headers={headers}
disabled={!showDropdown || activeInput != "state"}
inputName="state"
setFormData={setFormData}
/\>
\</div\>
\<div\>
\<label htmlFor="neighborhood"\>Bairro (5° Coluna)\</label\>
\<input
id="neighborhood"
type="text"
onChange={handleInputChange}
onClick={() =\> handleInputClick("neighborhood")}
onBlur={handleInputBlur}
value={formData.neighborhood}
name="neighborhood"
placeholder="Escreva o nome da sua coluna"
/\>
\<SpreadSheetOptions
headers={headers}
disabled={!showDropdown || activeInput != "neighborhood"}
inputName="neighborhood"
setFormData={setFormData}
/\>
\</div\>
\<div\>
\<label htmlFor="zipcode"\>Cep (6° Coluna)\</label\>
\<input
id="zipcode"
type="text"
list="headers"
onChange={handleInputChange}
onClick={() =\> handleInputClick("zipCode")}
onBlur={handleInputBlur}
name="zipCode"
value={formData.zipCode}
placeholder="Escreva o nome da sua coluna"
/\>
\<SpreadSheetOptions
headers={headers}
disabled={!showDropdown || activeInput != "zipCode"}
inputName="zipCode"
setFormData={setFormData}
/\>
\</div\>
\</div\>
\</div\>
\</CarouselSlide\>
\</Carousel\>
\<div className={styles\["form-controls"\]}\>
\<div className={styles\["checkbox-container"\]}\>
\<label htmlFor="checkbox" className={styles\["checkbox-label"\]}\>
Viagem de ida e volta
\</label\>
\<input
              id="checkbox"
              type="checkbox"
              checked={roundTrip}
              onChange={toggleCyclicalRoute}
            /\>
\</div\>
\<button className={styles\["upload-button"\]} disabled={disableUpload}\>
\<span\>Enviar\</span\>
\<FaCloudUploadAlt className={styles\["upload-icon"\]} /\>
\</button\>
\</div\>
\</form\>
\</Card\>
);
}

export default UploadCard;
import { Dispatch, SetStateAction } from "react";
import styles from "./SpreadSheetOptions.module.css";
import { FormData } from "./UploadCard";

type SpreadSheetOptionsProps = {
headers: string\[\];
disabled: boolean;
inputName: string;
setFormData: Dispatch\<SetStateAction\<FormData\>\>;
};

const SpreadSheetOptions = ({
headers,
disabled,
inputName,
setFormData,
}: SpreadSheetOptionsProps) =\> {
const active = !disabled && headers.length \> 0;

function handleAddressSelect(option: string): void {
console.log(event)
console.log('teste')
console.log(option);
console.log('teste')

    if (inputName == "street")
      setFormData((prev) => ({
        ...prev,
        street: option,
      }));
    
    if (inputName == "number")
      setFormData((prev) => ({
        ...prev,
        number: option,
      }));
    
    if (inputName == "city")
      setFormData((prev) => ({
        ...prev,
        city: option,
      }));
    
    if (inputName == "state")
      setFormData((prev) => ({
        ...prev,
        state: option,
      }));
    
    if (inputName == "neighborhood")
      setFormData((prev) => ({
        ...prev,
        neighborhood: option,
      }));
    
    if (inputName == "zipCode")
      setFormData((prev) => ({
        ...prev,
        zipCode: option,
      }));

}

return (
\<div className={`${styles["dropdown"]}`}\>
{active && (
\<ul className={`${styles["dropdown-menu"]} ${styles["active"]}`}\>
{headers.map((address) =\> {
return (
\<li
key={address + inputName}
className={styles\["address"\]}
onClick={() =\> handleAddressSelect(address)}
\\>
\<div className={`${styles["content"]}`}\>
<a href="#">{address}</a>
\</div\>
</li>
);
})}
</ul>
)}
\</div\>
);
};

export default SpreadSheetOptions;

这是我的 SpreadSheetComponent 的 css

.dropdown {
position: absolute;
width: 100%;
opacity: 100%;
display: flex;
justify-content: space-between;
}

.dropdown span {
font-size: x-large;
font-weight: 700;
color: var(--primary-color);
}

.dropdown-menu {
position: absolute;
display: flex;
flex-direction: column;
list-style: none;
background-color: white;
height: 100px;
width: 90%;
border: 1px solid var(--font-medium);
overflow-y: auto;
border-radius: 0.25rem;
transform: translateY(-10px);
opacity: 0;
transition:
transform 300ms ease-out,
opacity 300ms ease-out;
z-index: -1;
}

.dropdown-menu.active {
transform: translateY(0);
opacity: 1;
z-index: 1;
}

.dropdown-menu .address {
width: 100%;
font-size: 0.85rem;
color: var(--primary-color);
background-color: white;
padding: 0.25rem 1rem;

transition:
background 200ms ease-in,
color 200ms ease-in;
}

.dropdown-menu .address h2 {
font-weight: 700;
font-size: medium;
color: var(--primary-color);
border: none;
}

.dropdown-menu .address h2:hover {
color: white;
}

.dropdown-menu .content {
display: flex;
align-items: center;
justify-content: space-between;
}

.dropdown-menu .address a {
outline: none;
display: inline-block;
width: 100%;
height: 100%;
text-decoration: none;
background-color: inherit;
color: inherit;
}

.dropdown-menu li a {
display: block;
padding: 0.25rem;
color: var(--primary-color);
margin: 0.1em 0;
text-decoration: none;
}

.dropdown-menu .address:hover {
background-color: var(--primary-color);
color: white;
cursor: pointer;
}

.content a {
font-size: 0.85rem;
}

我尝试使用控制台日志进行调试,检查了状态行为,检查了 css 样式。我真的不知道什么会导致这个问题。

css typescript next.js
1个回答
0
投票

在您提供的代码中存在各种不正确的

\
字符。这是一个例子

onClick={() =\> handleAddressSelect(address)}

正确的语法是

onClick={() => handleAddressSelect(address)}
© www.soinside.com 2019 - 2024. All rights reserved.