score:2

Accepted answer

there's quite a bit going on your codesandbox example, but by stripping it down its bare bones, i was able to track down the issue...

safari doesn't seem to support input elements that try to use the oninput event listener -- the callback is never executed. instead, you can use the onchange event listener.


for the example below, i faked an api call by setting a promise with a timeout, but this not needed and is only for demonstration purposes. in addition, i like using objects over multiple individual states, especially when the state needs to be synchronous -- it also is cleaner, easier to read, and functions more like a class based component.

demo: https://jd13t.csb.app/

source:

edit image upload preview


components/detailpage.js

import react, { useref, usestate } from "react";
import { circularprogress, icon, fab } from "@material-ui/core";

const initialstate = {
  isloading: false,
  imagename: "",
  imagepreview: null,
  imagesize: 0
};

const enterdetailpage = () => {
  const [state, setstate] = usestate(initialstate);
  const uploadinputel = useref(null);

  const handleuploadchange = async ({ target: { files } }) => {
    setstate(prevstate => ({ ...prevstate, isloading: true }));

    const file = files[0];
    await new promise(res => {
      settimeout(() => {
        res(
          setstate(prevstate => ({
            ...prevstate,
            imagename: file.name,
            imagepreview: url.createobjecturl(file),
            imagesize: file.size,
            isloading: false
          }))
        );
      }, 2000);
    });
  };

  const resetupload = () => {
    setstate(initialstate);
    uploadinputel.current.value = null;
  };

  const uploadimage = async () => {
    if (state.imagepreview)
      setstate(prevstate => ({ ...prevstate, isloading: true }));

    await new promise(res => {
      settimeout(() => {
        res(alert(json.stringify(state, null, 4)));
        resetupload();
      }, 2000);
    });
  };

  const { imagepreview, imagename, imagesize, isloading } = state;

  return (
    <div style={{ padding: 20 }}>
      <div style={{ textalign: "center" }}>
        <div>
          <input
            accept="image/jpeg,image/gif,image/png"
            classname="hidden"
            id="button-file"
            type="file"
            ref={uploadinputel}
            onchange={handleuploadchange}
          />
          <label htmlfor="button-file">
            <div>
              {imagepreview ? (
                <>
                  <img
                    src={imagepreview}
                    alt="avatar"
                    style={{ margin: "0 auto", maxheight: 150 }}
                  />
                  <p style={{ margin: "10px 0" }}>
                    ({imagename} - {(imagesize / 1024000).tofixed(2)}mb)
                  </p>
                </>
              ) : (
                <icon fontsize="large" color="primary" classname="cloud-icon">
                  cloud_upload
                </icon>
              )}
            </div>
          </label>
          <fab
            variant="extended"
            size="large"
            color="primary"
            aria-label="add"
            classname=""
            type="button"
            onclick={uploadimage}
          >
            {isloading ? (
              <circularprogress style={{ color: "white" }} />
            ) : (
              "submit"
            )}
          </fab>
          {imagepreview && (
            <fab
              variant="extended"
              size="large"
              color="default"
              aria-label="add"
              classname=""
              type="button"
              onclick={resetupload}
            >
              cancel
            </fab>
          )}
        </div>
      </div>
    </div>
  );
};

export default enterdetailpage;

Related Query

More Query from same tag