是否错误地使用了useCallback和useEffect ?



我正在尝试使用react钩子构建一个上传文件,我仍然是react的初学者,我已经实现了上传文件,但我有一些问题,比如当我上传一个名为"A.png"我尝试再次上传相同的文件,onUpload函数没有被触发。直到我上传另一个文件,比如"B.png"我认为这是因为不正确地使用了useCallback和useEffect。这是我的镜头:

export const UploadAssets = (props) => {  
const [files, setFiles] = useState<FileList>();
const fileInput = useRef<HTMLInputElement>(null);
useEffect(() => {
//some code...
}, []);
const onChooseFile = (e) => {
e.preventDefault();
if (fileInput && fileInput.current) {
props.options.onStart();
fileInput.current.click();
}
}
const onChangeFile = (e) => {
e.preventDefault();
if (fileInput && fileInput.current && fileInput.current.files) {  
props.options.onChooseFile(fileInput.current.files);     
setFiles(fileInput.current.files);      
}
}
const onUpload = useCallback(() => {
if (!files) return false;

let formData = new FormData();
for (let i = 0; i < files.length; i++) {
const file = files[i];
formData.append(file.name, file);
}

const xhr = new XMLHttpRequest();
xhr.open('post', myUrl, true);
xhr.send(formData);
return true;
}, [files],
);
useEffect(() => {  
if (files) {
onUpload();
}
}, [files])

return (
<div>
<button onClick={onChooseFile}>
Upload File
</button>
<input type="file" ref={fileInput} onChange={onChangeFile} hidden/>
</div>
);
}

,据我所知,这不是一个好的做法,调用onUpload函数在onChangeFile。

上传一个名为"A.png"我尝试再次上传相同的文件,onUpload函数没有被触发。直到我上传另一个文件,比如"B.png"。

这是因为您只在files状态改变时调用onUpload:

useEffect(() => {  
if (files) {
onUpload();
}
}, [files])

useEffect在这种情况下只会调用传递给它的函数当files改变,所以如果你重新选择相同的文件,<input type="file" />将不会触发onchange事件和你的onChangeFile处理程序更新files状态将不会被调用。

你真的需要在状态中存储files吗?我不建议,当用户选择一个新文件时,你不需要额外的重新渲染。

我会试着把它改成这样:

export const UploadAssets = (props) => {  
const fileInput = useRef<HTMLInputElement>(null);
const onChooseFile = useCallback(() => {
e.preventDefault();
if (fileInput.current) {
props.options.onStart();
fileInput.current.click();
}
}, [props.options.onStart, fileInput])
const onUpload = useMemo(() => {
return files => {
if (!files) return false;

let formData = new FormData();
for (let i = 0; i < files.length; i++) {
const file = files[i];
formData.append(file.name, file);
}

const xhr = new XMLHttpRequest();
xhr.open('post', myUrl, true);
xhr.send(formData);
return true;
}
}, [])
const onChangeFile = useCallback(() => {
e.preventDefault();
if (fileInput.current && fileInput.current.files) {
props.options.onChooseFile(fileInput.current.files);
onUpload(fileInput.current.files);   
}
}, [props.options.onChooseFile, fileInput, onUpload])
useEffect(() => {
//some code...
}, []);

return (
<div>
<button onClick={onChooseFile}>
Upload File
</button>
<input type="file" ref={fileInput} onChange={onChangeFile} hidden/>
</div>
);
}
  • 这调用useCallback按钮点击事件处理程序,以保持处理程序的引用完整性跨重新呈现(虽然现在我删除了useState你可能不需要这个优化)。
  • 删除useState,因为它似乎没有必要。无论如何,您正在将选定的文件传递给prop回调。
  • onUpdate调用滚动到onChangeFile内部,并通过将其包装在useMemo内部来保留跨重新渲染的引用。
  • 在变量声明之后移动钩子调用。

尝试在触发onChange函数之前先清除输入字段

function clearInput(e){
e.target.value = ''
}
<input type="file" ref={fileInput} onChange={onChangeFile} onClick={clearInput} hidden/>

最新更新