read JSON file from local hard drive and parse into react app

Viewed 681

I am trying to read a JSON file from a hard drive that has been either selected by a user or dragged and dropped. I'm a little unsure how it should work and I think I have an issue with the fact that FileReader.readAsText is async. Anyways, made a drop zone component which works fine and users can also select a file. Here is the relevant parts of my code:

const DropZone = () => {
  const [files, setFiles] = useState([]);
  const [parsedFile, setParsedFile] = useState(null);

  const onDrop = async (e) => {
    e.preventDefault();
    if (disabled) return;
    setFiles([...e.dataTransfer.files]);

    const fileReader = new FileReader();

    await fileReader.readAsText(e.dataTransfer.files[0]);

    fileReader.onload = (e) => {
      setParsedFile(JSON.parse(e.target.result));
    };

    // e.dataTransfer.clearData();
    // setDragging(false);
  };

  const renderHasFiles = () => {
    // console.log('files: ', files);
    return (
      <Fragment>
        <StyledIcon iconName="cloud_done" size="extraLarge" color={alertColors.SUCCESS} />
        <div>Got em!</div>
        <FileList>
          <FileListHeader>
            <TableHeader>File Name</TableHeader>
            <TableHeader align="right">Size</TableHeader>
          </FileListHeader>
          <FileListContent>
            {renderFileList()}
          </FileListContent>
        </FileList>
        <ButtonContainer>
          <ClearButton onClick={(e) => {e.preventDefault(); e.stopPropagation(); setFiles([]);}}>
            Clear Files
          </ClearButton>
          <StyledButton onClick={(e) => { e.preventDefault(); e.stopPropagation(); submitButtonCallback(files, parsedFile); }}>
            {submitButtonText}
          </StyledButton>
        </ButtonContainer>
      </Fragment>
    );
  };

  return (
    <DropZoneContainer
      width={width}
      height={height}
      disabled={disabled}
      dragging={dragging}
      onClick={() => openFileDialog()}
      onDragOver={(e) => onDragOver(e)}
      onDrop={(e) => onDrop(e)}
    >
      {files.length === 0 ? renderNoFiles() : renderHasFiles()}
    </DropZoneContainer>
  );
};

The console log of parsedFile is null but if I set a debugger statement in the onload I can see the contents of the file being read. Why can't I get the contents of the file out?

2 Answers

You are trying to print parsedFile, but in the state is it called parsedFiled. You are also printing the value before the onload callback sets the state. Try moving the print into the callback function.

The onload callback function is asyncronous, so you can't use the value immediately after invoking it because there is no guarantee that the function has resolved. In order to use the file contents, you can do one of the following: Place the relevant code in the render function so it can react to the state change; write code and insert it into the callback function so that it is called when the contents are available; use a syncronous read function or wrap the asyncronous function in a promise, so that you can have an expectation about how it resolves (e.g. await the promise and use the contents immediately after).

typo in:

const [parsedFile, setParsedFile] = useState(null);

file reader code:

const fileReader = new FileReader();

fileReader.readAsText(e.dataTransfer.files[0]);

fileReader.onload = () => {
  setParsedFile(fileReader.result);
};
Related