CIB
CIB

Reputation: 785

React & TypeScript Upload File Input Error

I'm trying to submit a form with react & typescript but keep getting errors.

My terminal outputs this error Type 'File | undefined' is not assignable to type 'string | number | readonly string[] | undefined' and if I try to upload a file the website crashes with this warning and error.

Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.

Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string. at HTMLInputElement.set [as value]

-- EDIT --

So after debugging for a while I have discovered that if I just pass an empty string into the value attribute then the form submits and my image / file is processed and works fine. So the issue lyes with the setting of the value attribute which mean it's this error I need to fix! Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string. at HTMLInputElement.set [as value]

I have the following input component

import React, { useRef } from "react";
import { styled } from "../../../stitches.config";

const Input = styled("input", {
    width: "100%",
    height: "45px",
    background: "$white",
    color: "$black",
    textIndent: "10px",
    border: "1px solid $grey",
    borderRadius: "5px",
    paddingRight: "10px",
});

interface Props extends React.InputHTMLAttributes<HTMLInputElement> {
    onChange: React.ChangeEventHandler<HTMLInputElement>;
}

export default ({ type, name, value, required, onChange }: Props) => {
    const input = useRef<HTMLInputElement>(null);

    return (
        <Input
            type={type}
            name={name}
            value={value}
            ref={input}
            required={required}
            onChange={(e) => onChange(e)}
        />
    );
};

My form component looks like this

interface Props {
    auth: any;
}

interface FormProps {
    title: string;
    intro: string;
    content: string;
    image: File | undefined;
    published_date: string;
}

const Create = ({ auth }: Props) => {
    const { data, setData, processing, post, errors } = useForm<FormProps>({
        title: "",
        intro: "",
        content: "",
        image: undefined,
        published_date: "",
    });

    const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
        if (e.target.files) {
            setData("image", e.target.files[0]);
            console.log(data.image, " image");
        }
    };

    const onSubmit = (e: React.SyntheticEvent) => {
        e.preventDefault();
        post("/admin/blogs/create", {
            preserveScroll: true,
        });
    };

    return (
        <Authenticated auth={auth} css={{ background: "$light" }}>
            <Container css={{ maxWidth: "1200px", py: "100px" }}>
                <Heading size="charlie">Create a post</Heading>
                <Errors errors={errors} />

                <form onSubmit={onSubmit} noValidate>
                   
                    ....

                    <Row>
                        <Label input={"image"} value={"Image"} required={true}>
                            <Input
                                type="file"
                                name="image"
                                value={data.image}
                                required={true}
                                onChange={handleFile}
                            />
                            {errors.image && (
                                <InputError error={errors.image} />
                            )}
                        </Label>
                    </Row>
                    <Row>
                        <Button
                            processing={processing}
                            type={"submit"}
                            color={"primary"}
                        >
                            Publish
                        </Button>
                    </Row>
                </form>
            </Container>
        </Authenticated>
    );
};

export default Create;

Upvotes: 2

Views: 4808

Answers (1)

hangindev.com
hangindev.com

Reputation: 4873

Since the value of a file input <input type="file" /> is read-only, it can only be an uncontrolled element, which means it does not take value prop. docs

In your custom input element, you may want to check the type before passing the value prop.

Upvotes: 2

Related Questions