Javascript 如何使用react和typescript显示上载和上载的文件?

Javascript 如何使用react和typescript显示上载和上载的文件?,javascript,reactjs,typescript,Javascript,Reactjs,Typescript,我想上传文件到服务器上,并使用react和typescript在列表中显示它们 我想做什么? 我有两个部分,第一个列表和第二个列表。还有一个上传按钮,可以上传如下图所示的文件 当用户选择文件并单击上载按钮时,将显示用户已选择的文件,并且一旦上载开始,将显示正在上载的文件的进度条(一次仅上载一个文件)。上传文件后,文件将从文件状态中删除,附件状态将从服务器获取上传的文件 下面是我的代码 function List() { const [file, setFile] = React.use

我想上传文件到服务器上,并使用react和typescript在列表中显示它们

我想做什么?

我有两个部分,第一个列表和第二个列表。还有一个上传按钮,可以上传如下图所示的文件

当用户选择文件并单击上载按钮时,将显示用户已选择的文件,并且一旦上载开始,将显示正在上载的文件的进度条(一次仅上载一个文件)。上传文件后,文件将从文件状态中删除,附件状态将从服务器获取上传的文件

下面是我的代码

function List() {
    const [file, setFile] = React.useState({ name: null });
    const [files, setFiles] = React.useState([]);
    const [isUploading, setIsUploading] = React.useState(false);
    const [uploadProgress, setUploadProgress] = React.useState(0);
    const fileRef = React.useRef<HTMLInputElement>(null);
    const [getAttachmentUploadUrl] = useLazyQuery(GET_UPLOAD_URL, {
        onCompleted: (data: any) => uploadFile(data.getAttachmentUploadUrl),
    });
    useEffect(() => {
        if (files && files[0]) {
            if (files[0].size > 10000) {
                toast.error('File size limit');
                setFiles(files.slice(1));
            } else {
                setFile(files[0]);
                getAttachmentUploadUrl();
            }
        }
    }, [files, setFile, setFiles, getAttachmentUploadUrl]);


   const uploadFile = async (data: AttachmentUrlResponse) => {
       try {
           setIsUploading(true);

           await axios.put(data.uploadUrl, file, {
               onUploadProgress: (progressEvent: any) =>
               setUploadProgress(
                   Math.round((progressEvent.loaded * 100) / progressEvent.total)
               ),
           });

           const input: AttachmentCreateInput = {
               uploadId: data.uploadId,
               section: activeTab, //activeTab is the variable telling which 
    // tab has been selected by user first list or second list
    filename: file.name,
  };
           await addAttachement({
               variables: { input },
           });

           await refetchAttachment();

           setIsUploading(false);
           setUploadProgress(0);
           setFile({ name: null });
           setFiles(files.slice(1));
       } catch (e) {
           toast.error('File upload failed');
       }
   };


   const handleFilesDrop = (event: any) => {
       const { files } = event.type === 'drop' ? event.dataTransfer : event.target;
       if (files && files.length > 0) {
           handleUploadedFile(files);
       }
   };

   const handleUploadedFile = async (files: any) => {
       setFiles(Object.values(files)); 
   };

   const uploadClickHandler = () => {
       if (fileRef.current) fileRef.current.click();
   };

   const handleFileChange = (event: React.SyntheticEvent) => {
       event.persist();
       event.preventDefault();

       const { files } = event.target as HTMLInputElement;
       if (files && !isUploading) handleUploadedFile(files);
   };

   const { data: attachments, refetch: refetchattachments } = useQuery(
       LIST_ATTACHMENTS
   );


   return (
       <>
           <Container>
               <Tab>
                   <span>First list</span>
               </Tab>
               <Tab>
                   <span>Second list</span>
               </Tab>
               <FileListContainer>
                   {isUploading && files && //this shows files that is to be
                       // uploaded yet

                       files.map((file, index) => (
                           <span>{file.name}</span>
                               {index=== 0 && <ProgressBar>}
                       )
                   }
                   {attachments && attachments.data.filter( //this has files 
                       // that is uploaded and retrieved from server
                       (attachment: Attachment) =>
                           attachment.section === activeTab
                       )
                       .map((attachment,index) => (
                           <span>{attachment.filename}</span>
                       )
                   }
               </FileListContainer>
               <FileUpload onClick={handleFileChange}/>
           </Container>
       </>
   );
}
函数列表(){
const[file,setFile]=React.useState({name:null});
const[files,setFiles]=React.useState([]);
const[isupload,setisupload]=React.useState(false);
const[uploadProgress,setUploadProgress]=React.useState(0);
const fileRef=React.useRef(null);
const[getAttachmentUploadUrl]=useLazyQuery(获取上传URL{
onCompleted:(data:any)=>uploadFile(data.getAttachmentUploadUrl),
});
useffect(()=>{
如果(文件和文件[0]){
如果(文件[0]。大小>10000){
toast.error(“文件大小限制”);
setFiles(files.slice(1));
}否则{
setFile(文件[0]);
getAttachmentUploadUrl();
}
}
},[files,setFile,setFiles,getAttachmentUploadUrl];
const uploadFile=async(数据:AttachmentUrlResponse)=>{
试一试{
setisupload(true);
等待axios.put(data.uploadUrl,文件{
onUploadProgress:(progressEvent:any)=>
设置加载进度(
数学圆((progressEvent.loaded*100)/progressEvent.total)
),
});
常量输入:AttachmentCreateInput={
uploadId:data.uploadId,
section:activeTab,//activeTab是告诉
//选项卡已由用户选择第一个列表或第二个列表
filename:file.name,
};
等待添加附件({
变量:{input},
});
等待重新设置附件();
setisupload(false);
设置加载进度(0);
setFile({name:null});
setFiles(files.slice(1));
}捕获(e){
toast.error('文件上载失败');
}
};
const handleFilesDrop=(事件:any)=>{
const{files}=event.type=='drop'?event.dataTransfer:event.target;
如果(files&&files.length>0){
handleUploadedFile(文件);
}
};
const handleUploadedFile=async(文件:any)=>{
setFiles(Object.values(files));
};
常量uploadClickHandler=()=>{
如果(fileRef.current)fileRef.current.click();
};
const handleFileChange=(事件:React.SyntheticEvent)=>{
event.persist();
event.preventDefault();
const{files}=event.target作为HTMLInputElement;
如果(文件&正在上载)handleUploadedFile(文件);
};
const{data:attachments,refetch:refetchattachments}=useQuery(
列出附件
);
返回(
第一名单
第二份名单
{isUploading&&文件&&&//这显示要上载的文件
//上传了吗
files.map((文件,索引)=>(
{file.name}
{index==0&&}
)
}
{attachments&&attachments.data.filter(//这有个文件
//从服务器上载和检索的
(附件:附件)=>
附件.section==activeTab
)
.map((附件、索引)=>(
{attachment.filename}
)
}
);
}
上面的代码工作正常。但问题是,由于状态文件具有用户选择的初始文件和未上载的文件以及附件已上载的文件,并且两者都将显示在FileListContainer中,因此在文件列表UI中会看到一些闪烁,即当一个文件上载完成时,其他尚未上载的文件将看不到一会儿

所以我想重新编码这个逻辑,从单个数组中读取和显示文件,而不是从文件和附件中读取和显示两个文件。但我不知道该怎么做。我对编程特别是这类编程是新手

有人能帮我吗?谢谢


文件上载完成后,上载队列中的文件将在一秒钟内不显示

这是因为您在上传时将
isUploading
设置为
false

  setIsUploading(false);
  setUploadProgress(0);
但是只有当
isUploading
为true时,才显示队列中的文件

{isUploading && files && files.map(...)}
因此,这可以防止文件在
uploadDone
startNextFileUpload
持续时间之间呈现。 因此,您可以在渲染时删除
isUploading
条件,如下所示:

{files && files.map(...)}

文件上载完成后,上载队列中的文件将在一秒钟内不显示

这是因为您在上传时将
isUploading
设置为
false

  setIsUploading(false);
  setUploadProgress(0);
但是只有当
isUploading
为true时,才显示队列中的文件

{isUploading && files && files.map(...)}
因此,这可以防止文件在
uploadDone
startNextFileUpload
持续时间之间呈现。 因此,您可以在渲染时删除
isUploading
条件,如下所示:

{files && files.map(...)}

当一个文件上载完成时,其他尚未上载的文件将无法查看