I only came across very hacky solutions, so maybe someone has a better idea how to solve it.
Here's what I'm trying to do:
UploadComponent
lets the user select files using refs
.FilePreviewComponent
displays the uploaded files and allows the
user to delete a file. When a file is deleted, I want to reset the
file input refs in the UploadComponent so that the same file can be
uploaded again.For now I couldn't find any solution to reset refs cross-component. Here is code example:
const App = () => {
const { control, handleSubmit, setValue, getValues } = useForm();
const [selectedFiles, setSelectedFiles] = useState([]);
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* Upload Component */}
<Controller
name="files"
control={control}
render={({ field: { value, onChange } }) => (
<UploadComponent
onFileSelected={(files) => {
const updatedFiles = [...value, ...files];
onChange(updatedFiles);
setSelectedFiles(updatedFiles);
}}
/>
)}
/>
{/* File Preview Component */}
<Controller
name="files"
control={control}
render={({ field: { value } }) => (
<FilePreviewComponent
files={value}
onDelete={(fileToDelete) => {
const updatedFiles = value.filter((file) => file.uri !== fileToDelete.uri);
setSelectedFiles(updatedFiles);
setValue("files", updatedFiles);
}}
/>
)}
/>
<button type="submit">Submit</button>
</form>
);
};
export default App;
And here is the upload component.
const UploadComponent = ({ onFileSelected }) => {
const fileInputRef = useRef(null);
const handleFileUpload = (e) => {
const files = Array.from(e.target.files).map((file) => ({
uri: URL.createObjectURL(file), // Create a temporary URL for preview
name: file.name,
type: file.type,
blob: file,
}));
onFileSelected(files);
};
return (
<div>
{/* Button to trigger file input */}
<button onClick={() => fileInputRef.current.click()}>Upload File</button>
{/* Hidden file input triggered by button */}
<input
ref={fileInputRef}
type="file"
accept="image/*,application/pdf"
style={{ display: "none" }}
onChange={handleFileUpload}
/>
</div>
);
};
export default UploadComponent;
The issue reported here may not be related with ref. It may be an issue related with input element itself.
What is meant here is, a retry of a file upload also suffers from the same issue as it is not allowed. Furthermore, when a file has been uploaded, but before confirming the form, the same file needs to be uploaded again - due to some reason. The below html copied from the post may not support this use case.
<input
ref={fileInputRef}
type="file"
accept="image/*,application/pdf"
style={{ display: "none" }}
onChange={handleFileUpload}
/>
However the same use case may be possible when the value property is also used. It can also be done by manipulating the event object as well.
Please see below a sample code below, showing the same.
App.js
export default function App() {
return (
<>
<label>Retry a same file upload : Disallowed</label>
<input type="file" onChange={(e) => console.log(e.target.value)}></input>
<br></br>
<label>Retry a same file upload : allowed method 1</label>
<input
type="file"
value=""
onChange={(e) => console.log(e.target.value)}
></input>
<br></br>
<label>Retry a same file upload : allowed method 2</label>
<input
type="file"
onChange={(e) => {
console.log(e.target.value);
e.target.value = '';
}}
></input>
<br></br>
</>
);
}
Test plan
After the app has been loaded, each of three upload buttons will be clicked twice to upload a same file.
Test results
The console logs generated
// C:\somefile.txt - logged only once
// C:\somefile.txt - logged twice
// C:\somefile.txt - logged twice
Observation
The logs show that, the first input declaration does not allow retry. However, the next two input declarations do support that. Of which, the first one is implemented by value property, and the second one is by manipulating value property through scripting.
Solution proposal
Perhaps after applying one of the options discussed above, the reported issue may be resolved. The reason for this proposal is, as we know, ref is just an access to a DOM element, it does not have anything of its own. Therefore if the DOM element is capable of doing something, then the ref to it will also be reflecting the same.
Citation: