Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript and React with File Upload (typing)

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>
like image 970
MattoMK Avatar asked Oct 22 '20 18:10

MattoMK


People also ask

Does React work well with TypeScript?

Using TypeScript with React provides better IntelliSense and code completion for JSX.

How to access file list in react JS and typescript?

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.

How do I add typings to TypeScript?

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.

Does TypeScript support react JSX?

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:

Can I create a type definition file for a React component?

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.


1 Answers

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

like image 179
Linda Paiste Avatar answered Oct 19 '22 14:10

Linda Paiste