I just started with TypeScript, so please bear in mind. I am trying to implement a simple file upload in React/TS. In general, I don't think I understand how to initialize objects, like in useState, and handle the possibilities properly. For example, consider this code where I am running a function when a user clicks an Upload button and I try to read the file from state and put in the formData to send it to my api endpoint):
const [fileSelected, setFileSelected] = React.useState<File>() // also tried <string | Blob>
const handleImageChange = function (e: React.ChangeEvent<HTMLInputElement>) {
const fileList = e.target.files;
if (!fileList) return;
setFileSelected(fileList[0]);
};
const uploadFile = function (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) {
const formData = new FormData();
formData.append("image", fileSelected, fileSelected.name);
// line above ^ gives me the error below
};
}
Argument of type 'string | Blob | undefined' is not assignable to parameter of type 'string | Blob'. Type 'undefined' is not assignable to type 'string | Blob'.ts(2345)
How are you supposed to initialize your objects in useState? And if you don't initialize, like here, do you have to keep checking for null to keep the compiler happy? I just added a check in UploadFile to see if fileSelected is null. But then I had to do it again to reference fileSelected.name.
So for objects, particularly something like this File type, how should you handle initializing it? But also, in general, how to handle object types?
Not that it matters, but here's the markup part:
<Container className={classes.container}>
<label htmlFor="photo">
<input
accept="image/*"
style={{ display: "none" }}
id="photo"
name="photo"
type="file"
multiple={false}
onChange={handleImageChange}
/>
<Button
className={classes.buttons}
component="span"
variant="contained"
onClick={uploadFile}
>
Choose Picture
</Button>
</label>
</Container>
Using TypeScript with React provides better IntelliSense and code completion for JSX.
With React JS and TypeScript I found that I prefer to use the arrow function with the onChange attribute on the input element. From there you can access the files and pass them to a function. In that function you can just add an argument of type “FileList”. This is a really simple problem but it took me much longer than it should have.
When adding typings, the easiest way for TypeScript to recognize it, is by adding a key to the package.json file. Like so: In this case, I'm assuming that when the package is deployed there will be a folder with an index.d.ts file in it.
TypeScript supports JSX and can correctly model the patterns used in React codebases like useState. Today there are many frameworks which support TypeScript out of the box:
Fear not, creating a type definition file is easy, and because you have already saved some time at work using a component built by somebody else, maybe you can use that time and collaborate creating the type definition file for the component. In my case, I was working with the react-selectize component.
When you call useState
without setting an initial/default value then the type will include undefined
in addition to the expected type. Here you've used the generic <File>
to tell useState
what type to expect. But because you initiated it without setting a value, the type of fileSelected
becomes File | undefined
.
This is fine because it accurately represents the reality that there will not always be a File
object in the state. But it does mean that we have to check the value of fileSelected
before using it. Just add an if
statement and your formData.append()
call won't have any issues at runtime or in the typescript compiler. The interface File
extends Blob
so there is no issue assigning it to string | Blob
after we've ruled out the possibility of undefined
.
const uploadFile = function (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) {
if (fileSelected) {
const formData = new FormData();
formData.append("image", fileSelected, fileSelected.name);
}
};
Playground Link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With