2

I would like to run the following code to have the file selector input a file and manage the input file as a state in the file. But it produces output as described in the comments. I do not know the cause. How should I fix the problem?

const [file, setFile] = useState<File | null>(null);

const fileSelect = (event: ReactChangeEvent<HTMLInputElement>) => {
  if (event.target.files === null) { return ; }
  const f: File | null = event.target.files[0];
  console.log(typeof(f)); // <- object
  setFile(f);
  console.log(f); // <- null
}

return (
<div className="flex px-1">
<input
  onChange={fileSelect}
  type="file"
/>
</div>
);
musako
  • 897
  • 2
  • 10
  • 26
  • Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – Konrad Apr 12 '23 at 06:49
  • if you want see file state changes on console use ``useEffect` hook : https://codesandbox.io/s/distracted-ace-p226r8?file=/src/App.js – Abbas Bagheri Apr 12 '23 at 06:58
  • Hi @BoKuToTsuZenU. I could not see the comments to which you referred in your question. But, I believe I have the solution to your problem. My code is in JavaScript. You will need to update it to Typescript. Please see my answer, and let me know if you have any questions. – kofeigen Apr 13 '23 at 01:24

2 Answers2

2

This is because when you call setState it doesn't mutate the state immediately. You can refer to this thread here. Why does calling react setState method not mutate the state immediately?

Jun Yan
  • 36
  • 2
1

Based on your Typescript data types, it appears you want to save the actual file contents into state, not just the name of the file.

These lines in your code:

const f: File | null = event.target.files[0];
console.log(typeof(f)); // <- object
setFile(f);

...show an attempt to update state with type File, not type string.

Saving file contents into state can be achieved with React.useState. File contents can be converted to Base64 and stored in state as strings.

These demos on JS Fiddle use React.useState and Redux Toolkit. Source code is available on SourceHut: React.useState; Redux Toolkit.

The demos use the FileReader: readAsDataURL() method to upload files as Data URLs which can be saved as strings (not strictly as File types) in an array using React.useState.

These functions are the key to saving file content:

Read the file as a Data URL and update state

function processFiles( file ) {
    var filereader = new FileReader();
    filereader.onload = function onLoadCallbck( fileEvent ) {
        let dataUrl = fileEvent.target.result;
        let newFile = {
            dataUrl,
            fileName: file.name,
            fileType: file.type
        };
        setFiles( ( prev ) => {
            let list = [ ...prev ];
            let newList = list.concat( newFile );
            return newList;
        } );
    }

    filereader.readAsDataURL( file );
}

Event handler for file upload field

/**
 * Event listener for the file upload input field.
 */
function handleFiles( event ) {
    if ( ! event.target.files.length ) {
        listRef.current.innerHTML = "<p>No files selected!</p>";
    } else {
        listRef.current.textContent = "";
        Array.prototype.forEach.call( event.target.files, processFiles );
    }
}
kofeigen
  • 1,331
  • 4
  • 8