saint-bernard
TypeScript icon, indicating that this package has built-in type declarations

3.3.0 • Public • Published

saint-bernard

React Hook for requesting data using the Web API Fetch written in TypeScript

NPM Coverage Status Vulnerabilities Size Types

Summary

Features

  • Close to the metal, configurable yet high-level enough to help you do more with less
  • Tested to cover 100% of the source-code published
  • Zero-dependencies
  • Lightweight
  • Written in TypeScript from the ground up
  • Strict semantic versionning for the releases
  • Best when used with zod
  • Leveraging the Web API Fetch
  • Full control over the options, url, path & query parameters
  • Ability to cancel requests at any time
  • Written to fully work with modern React Hooks and functional components

Back to summary.

Requirements

Back to summary.

Installation

npm install --save --save-exact saint-bernard

Back to summary.

Uninstallation

npm uninstall saint-bernard

Back to summary.

Usage

Stateful

state

import React from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { state } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  return (
    <ul>
      {state.map(user => (
        <li key={user.id}>
          {user.email}
        </li>
      ))}
    </ul>
  );
};

Back to summary.

setState

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { setState } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    setState([]);
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

request

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { request } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    request({
      url: "https://jsonplaceholder.typicode.com/posts",
      method: "GET",
      headers: {
        "Accept": "application/json"
      },
      onResponse: async response => {
        const users = await response.json();

        return users;
      }
    });
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

cancel

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { cancel } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    return () => {
      cancel();
    };
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

timeout

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { request } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    request({
      url: "https://jsonplaceholder.typicode.com/users",
      timeoutInMilliseconds: 1000,
      onResponse: async response => {
        return [];
      }
    });
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

error

import React from "react";
import { useStatefulRequest, CancelError } from "saint-bernard";

export const App = () => {
  const { error } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  if (error) {
    if (error instanceof CancelError) {
      return <h1>Request was cancelled</h1>;
    }

    return <h1>Something went wrong: {error.message}</h1>;
  }

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

setError

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { setError } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    setError(new Error("Something went wrong"));
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

loading

import React from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { loading } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  if (loading) {
    return <h1>Loading...</h1>;
  }

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

initialLoading

import React from "react";
import { request } from "saint-bernard";

export const App = () => {
  const { loading } = useStatefulRequest<Array<any>>({
    initialState: [],
    initialLoading: true
  });

  if (loading) {
    return <h1>Loading...</h1>;
  }

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

setLoading

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { setLoading } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    setLoading(true);
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

abortController

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { abortController } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    abortController.abort();
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

setAbortController

import React, { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";

export const App = () => {
  const { setAbortController } = useStatefulRequest<Array<any>>({
    initialState: []
  });

  useEffect(() => {
    setAbortController(new AbortController());
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

Stateless

request

import React, { useEffect } from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { request } = useStatelessRequest();

  useEffect(() => {
    request({
      url: "https://jsonplaceholder.typicode.com/posts",
      method: "POST",
      headers: {
        "Accept": "application/json"
      },
      body: JSON.stringify({
        email: "user@domain.com"
      }),
      onResponse: async response => {
        if (response.ok) {
          console.log("Cool!");
        } else {
          console.log("Uncool...")
        }
      }
    });
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

cancel

import React, { useEffect } from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { cancel } = useStatelessRequest();

  useEffect(() => {
    return () => {
      cancel();
    };
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

timeout

import React, { useEffect } from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { request } = useStatelessRequest();

  useEffect(() => {
    request({
      url: "https://jsonplaceholder.typicode.com/users",
      timeoutInMilliseconds: 1000
    });
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

error

import React from "react";
import { useStatelessRequest, CancelError } from "saint-bernard";

export const App = () => {
  const { error } = useStatelessRequest();

  if (error) {
    if (error instanceof CancelError) {
      return <h1>Request was cancelled</h1>;
    }

    return <h1>Something went wrong: {error.message}</h1>;
  }

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

setError

import React, { useEffect } from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { setError } = useStatelessRequest();

  useEffect(() => {
    setError(new Error("Something went wrong"));
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

loading

import React from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { loading } = useStatelessRequest();

  if (loading) {
    return <h1>Loading...</h1>;
  }

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

initialLoading

import React from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { loading } = useStatelessRequest({
    initialLoading: true
  });

  if (loading) {
    return <h1>Loading...</h1>;
  }

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

setLoading

import React, { useEffect } from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { setLoading } = useStatelessRequest();

  useEffect(() => {
    setLoading(true);
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

abortController

import React, { useEffect } from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { abortController } = useStatelessRequest();

  useEffect(() => {
    abortController.abort();
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

setAbortController

import React, { useEffect } from "react";
import { useStatelessRequest } from "saint-bernard";

export const App = () => {
  const { setAbortController } = useStatelessRequest();

  useEffect(() => {
    setAbortController(new AbortController());
  }, []);

  return (
    <h1>Saint-Bernard</h1>
  );
};

Back to summary.

Examples

Dependent requests

import { useEffect } from "react";
import { useStatefulRequest } from "saint-bernard";
import { z } from "zod";

const commentsSchema = z.array(z.object({
  postId: z.number()
}));

const postSchema = z.object({
  userId: z.number()
});

const userSchema = z.object({
  username: z.string()
});

type Comments = z.infer<typeof commentsSchema>;
type Post = z.infer<typeof postSchema>;
type User = z.infer<typeof userSchema>;

export const App = () => {
  const {
    state: comments,
    loading: getCommentsRequestLoading,
    error: getCommentsRequestError,
    request: getCommentsRequest,
  } = useStatefulRequest<Comments | null>({
    initialState: null
  });

  const {
    state: post,
    request: getPostRequest,
    loading: getPostRequestLoading,
    error: getPostRequestError
  } = useStatefulRequest<Post | null>({
    initialState: null
  });

  const {
    state: user,
    request: getUserRequest,
    loading: getUserRequestLoading,
    error: getUserRequestError
  } = useStatefulRequest<User | null>({ initialState: null });

  useEffect(() => {
    getCommentsRequest({
      url: "https://jsonplaceholder.typicode.com/comments",
      method: "GET",
      headers: {
        Accept: "application/json"
      },
      onResponse: async response => {
        if (!response.ok) {
          throw new Error("Failed requesting comments");
        }

        const json = await response.json();
        const comments = commentsSchema.parse(json);

        return comments;
      }
    });
  }, []);

  useEffect(() => {
    if (!comments) {
      return;
    }

    if (comments.length === 0) {
      return;
    }

    const firstComment = comments[0];

    getPostRequest({
      url: `https://jsonplaceholder.typicode.com/posts/${firstComment.postId}`,
      method: "GET",
      headers: {
        Accept: "application/json"
      },
      onResponse: async response => {
        if (!response.ok) {
          throw new Error("Failed requesting a post");
        }

        const json = await response.json();
        const post = postSchema.parse(json);

        return post;
      }
    });

  }, [comments]);

  useEffect(() => {
    if (!post) {
      return;
    }

    getUserRequest({
      url: `https://jsonplaceholder.typicode.com/users/${post.userId}`,
      method: "GET",
      headers: {
        Accept: "application/json"
      },
      onResponse: async response => {
        if (!response.ok) {
          throw new Error("Failed requesting a user");
        }

        const json = await response.json();
        const user = userSchema.parse(json);

        return user;
      }
    });
  }, [post]);

  if (getCommentsRequestLoading) {
    return "Requesting comments, please wait...";
  }

  if (getPostRequestLoading) {
    return "Requesting a post, please wait...";
  }

  if (getUserRequestLoading) {
    return "Requesting a user, please wait...";
  }

  if (getCommentsRequestError) {
    return getCommentsRequestError.message;
  }

  if (getPostRequestError) {
    return getPostRequestError.message;
  }

  if (getUserRequestError) {
    return getUserRequestError.message;
  }

  if (!user) {
    return "No user found";
  }

  return (
    <ul>
      <li>
        Username: {user.username}
      </li>
    </ul>
  );
}

Back to summary.

Changelog

See CHANGELOG.md.

Back to summary.

Code of conduct

See CODE_OF_CONDUCT.md.

Back to summary.

License

See LICENSE.

Back to summary.

Security

See SECURITY.md.

Back to summary.

Contributing

See CONTRIBUTING.md.

Back to summary.

Package Sidebar

Install

npm i saint-bernard

Weekly Downloads

6

Version

3.3.0

License

MIT

Unpacked Size

26.1 kB

Total Files

6

Last publish

Collaborators

  • aminnairi