Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add useDownloadBlob hook #55

Merged
merged 14 commits into from
Feb 2, 2024
57 changes: 57 additions & 0 deletions docs/hooks/useDownloadBlob.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
tags:
- hook
---

# `useDownloadBlob` Hook

`useDownloadBlob` hook is used to directly download a file as bytes from Firebase Storage. Instead of [`useDownloadLink` hook](./useDownloadLink.md), this hook might be useful to process a file byte by byte instead of directly downloading to the user's machine.

A very simple example would be:

```typescript
const reference = ref(storage, "path/to/remote/file.png");
const { dispatch } = useDownloadBlob({ reference });
const blob = await dispatch(file);
```

!!! warning
`useDownloadBlob` is lazy by default and will not do anything until you use `dispatch` function.

You can get the state of the progress with this example.

```typescript
const { state } = useDownloadBlob({ reference });
const blob = await dispatch();
// `state` is "ready" | "loading" | "done"
```

`dispatch` method will return an instance of [`Blob`][BlobDoc], but additionally, you can also listen to [`Blob`][BlobDoc] from `useDownloadBlob` hook as well:

```typescript
const { blob } = useDownloadBlob({ reference });
// blob updates and rerenders when the state is `"done"`
// until then, it is `undefined`
await dispatch();
```

## Input Parameters

Input parameters for `useDownloadBlob` hook is as follows:

| Name | Type | Description | Required | Default Value |
|---|---|---|---|---|
| `reference` | [`firebase/storage/StorageReference`][StorageReferenceRefDoc] | Reference to a file in Storage. | ✅ | - |

## Return Type

`useDownloadBlob` hook returns an object with properties as below:

| Name | Type | Description |
|---|---|---|
| `blob` | [`Blob`][BlobDoc] or `undefined` | The blob of remote file to read. `undefined` if the `state` is not `"done"`. |
| `state` | `"ready" | "loading" | "done"` | The state of the process. |
| `dispatch` | `(maxDownloadSizeBytes? number) => Promise<string>` | A callback to start the process and return the link. You can also define how many bytes to download at most. |

[StorageReferenceRefDoc]: https://firebase.google.com/docs/reference/js/storage.storagereference
[BlobDoc]: https://developer.mozilla.org/en-US/docs/Web/API/Blob
3 changes: 2 additions & 1 deletion docs/storage/downloading-a-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

You can use one of the following ways to download a file from Firebase Storage:

- [`useDownloadLink` hook](../hooks/useDownloadLink.md)
- [`useDownloadLink` hook](../hooks/useDownloadLink.md) to get the link to download
- [`useDownloadBlob` hook](../hooks/useDownloadBlob.md) to directly download the bytes to process it yourself
- [`StorageDownloadLink` component](../components/StorageDownloadLink.md)
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ nav:
- hooks/useUploadFile.md
- hooks/useUploadFileResumable.md
- hooks/useDownloadLink.md
- hooks/useDownloadBlob.md
- Components:
- Firestore:
- components/FirestoreDocument.md
Expand Down
2 changes: 2 additions & 0 deletions src/storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export * from "./useUploadFileResumable";
export * from "./useDownloadLink";

export * from "./StorageDownloadLink";

export * from "./useDownloadBlob";
17 changes: 17 additions & 0 deletions src/storage/useDownloadBlob.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2024 Eray Erdin
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

import { renderHook } from "@testing-library/react";
import { ref } from "firebase/storage";
import { useDownloadBlob } from ".";
import { storage } from "../firebase";

const reference = ref(storage, "files/README.md");

it("initially, useDownloadBlob hook should have ready state", async () => {
const { result } = renderHook(() => useDownloadBlob({ reference }));
const { state } = result.current;
expect(state).toBe("ready");
});
38 changes: 38 additions & 0 deletions src/storage/useDownloadBlob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2024 Eray Erdin
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

import { StorageReference, getBlob } from "firebase/storage";
import { useState } from "react";

type UseDownloadBlobParams = {
reference: StorageReference;
};

type UseDownloadBlobState = "ready" | "loading" | "done";
type UseDownloadBlobDispatcher = (
maxDownloadSizeBytes?: number,
) => Promise<Blob>;
type UseDownloadBlob = {
blob: Blob | undefined;
state: UseDownloadBlobState;
dispatch: UseDownloadBlobDispatcher;
};

export const useDownloadBlob = ({
reference,
}: UseDownloadBlobParams): UseDownloadBlob => {
const [state, setState] = useState<UseDownloadBlobState>("ready");
const [blob, setBlob] = useState<Blob | undefined>(undefined);

const dispatch: UseDownloadBlobDispatcher = async (maxDownloadSizeBytes) => {
setState("loading");
const blob = await getBlob(reference, maxDownloadSizeBytes);
setState("done");
setBlob(blob);
return blob;
};

Check warning on line 35 in src/storage/useDownloadBlob.ts

View check run for this annotation

Codecov / codecov/patch

src/storage/useDownloadBlob.ts#L30-L35

Added lines #L30 - L35 were not covered by tests

return { blob, state, dispatch };
};
Loading