Giter Club home page Giter Club logo

react-query-auth's Introduction

react-query-auth

NPM

Authenticate your react applications easily with React Query.

Introduction

Using React Query has allowed us to significantly reduce the size of our codebase by caching server state. However, we still need to consider where to store user data, which is a type of global application state that we need to access from many parts of the app, but is also a server state since it is obtained from a server. This library makes it easy to manage user authentication, and can be adapted to work with any API or authentication method.

Installation

$ npm install @tanstack/react-query react-query-auth

Or if you use Yarn:

$ yarn add @tanstack/react-query react-query-auth

Usage

To use this library, you will need to provide it with functions for fetching the current user, logging in, registering, and logging out. You can do this using the configureAuth function:

import { configureAuth } from 'react-query-auth';

const { useUser, useLogin, useRegister, useLogout } = configureAuth({
  userFn: () => api.get('/me'),
  loginFn: (credentials) => api.post('/login', credentials),
  registerFn: (credentials) => api.post('/register', credentials),
  logoutFn: () => api.post('/logout'),
});

With these hooks, you can then add authentication functionality to your app. For example, you could use the useUser hook to access the authenticated user in a component.

You can also use the useLogin, useRegister, and useLogout hooks to allow users to authenticate and log out.

function UserInfo() {
  const user = useUser();
  const logout = useLogout();

  if (user.isLoading) {
    return <div>Loading ...</div>;
  }

  if (user.error) {
    return <div>{JSON.stringify(user.error, null, 2)}</div>;
  }

  if (!user.data) {
    return <div>Not logged in</div>;
  }

  return (
    <div>
      <div>Logged in as {user.data.email}</div>
      <button disabled={logout.isLoading} onClick={logout.mutate({})}>
        Log out
      </button>
    </div>
  );
}

The library also provides the AuthLoader component that can be used to handle loading states when fetching the authenticated user. You can use it like this:

function MyApp() {
  return (
    <AuthLoader
      renderLoading={() => <div>Loading ...</div>}
      renderUnauthenticated={() => <AuthScreen />}
    >
      <UserInfo />
    </AuthLoader>
  );
}

NOTE: All hooks and components must be used within QueryClientProvider.

API Reference:

configureAuth:

The configureAuth function takes in a configuration object and returns a set of custom hooks for handling authentication.

The configureAuth configuration object:

A configuration object that specifies the functions and keys to be used for various authentication actions. It accepts the following properties:

  • userFn: A function that is used to retrieve the authenticated user. It should return a Promise that resolves to the user object, or null if the user is not authenticated.

  • loginFn: A function that is used to log the user in. It should accept login credentials as its argument and return a Promise that resolves to the user object.

  • registerFn: A function that is used to register a new user. It should accept registration credentials as its argument and return a Promise that resolves to the new user object.

  • logoutFn: A function that is used to log the user out. It should return a Promise that resolves when the logout action is complete.

  • userKey: An optional key that is used to store the authenticated user in the react-query cache. The default value is ['authenticated-user'].

The configureAuth returned object:

configureAuth returns an object with the following properties:

  • useUser: A custom hook that retrieves the authenticated user. It is a wrapper around useQuery that uses the userFn and userKey specified in the configAuth configuration. The hook accepts the same options as useQuery, except for queryKey and queryFn, which are predefined by the configuration.

  • useLogin: A custom hook that logs the user in. It is a wrapper around useMutation that uses the loginFn specified in the configuration. The hook accepts the same options as useMutation, except for mutationFn, which is set by the configuration. On success, the hook updates the authenticated user in the React Query cache using the userKey specified in the configuration.

  • useRegister: A custom hook that registers a new user. It is a wrapper around useMutation that uses the registerFn specified in the configuration. The hook accepts the same options as useMutation, except for mutationFn, which is set by the configuration. On success, the hook updates the authenticated user in the React Query cache using the userKey specified in the configuration.

  • useLogout: A custom hook that logs the user out. It is a wrapper around useMutation that uses the logoutFn specified in the configuration. The hook accepts the same options as useMutation, except for mutationFn, which is set by the configuration. On success, the hook removes the authenticated user from the React Query cache using the userKey specified in the configuration.

  • AuthLoader:

    A component that can be used to handle loading states when fetching the authenticated user. It accepts the following props:

    • renderLoading: A function that is called when the authenticated user is being fetched. It should return a React node that is rendered while the user is being fetched.

    • renderUnauthenticated: A function that is called when the authenticated user is not authenticated. It should return a React node that is rendered when the user is not authenticated.

    • renderError: A function that is called when an error is thrown during the authentication request. It should return a React node that is rendered when the error occurs. It receives the Error object which can be used during rendering. Defaults to (error: Error) => <>{JSON.stringify(error)}</>.

    • children: A React node that is rendered when the authenticated user is successfully fetched.

If you need a more custom loading implementation, you can create your own AuthLoader component and use the useUser hook to fetch the authenticated user and handle the loading and error states yourself.

Examples:

To try out the library, check out the examples folder.

Contributing

  1. Clone this repo
  2. Create a branch: git checkout -b your-feature
  3. Make some changes
  4. Test your changes
  5. Push your branch and open a Pull Request

LICENSE

MIT

react-query-auth's People

Contributors

alan2207 avatar sirine-berguiga avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

react-query-auth's Issues

Example app does not start

I'm trying to get the example app to start (Mac Ventura / NodeJS v16.19.0) and I'm getting the following error(s):

Steps taken:

git clone https://github.com/alan2207/react-query-auth.git
cd react-query-auth/example/
npm install
npm start

Expected outcome: The example app should start without errors.

Actual outcome:

> [email protected] start
> cross-env NODE_ENV=production vite --port=3000

▲ [WARNING] Cannot find base config file "@tsconfig/create-react-app/tsconfig.json" [tsconfig.json]

    ../tsconfig.json:3:13:
      3 │   "extends": "@tsconfig/create-react-app/tsconfig.json",
        ╵              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

▲ [WARNING] Cannot find base config file "@tsconfig/create-react-app/tsconfig.json" [tsconfig.json]

    tsconfig.json:2:13:
      2 │   "extends": "@tsconfig/create-react-app/tsconfig.json"
        ╵              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


  vite v2.9.15 dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose

  ready in 153ms.

✘ [ERROR] [plugin vite:dep-scan] Failed to resolve entry for package "/Users/b/projects/playground/rqa/react-query-auth". The package may have incorrect main/module/exports specified in its package.json: Failed to resolve entry for package "/Users/b/projects/playground/rqa/react-query-auth". The package may have incorrect main/module/exports specified in its package.json.

    node_modules/vite/dist/node/chunks/dep-689425f3.js:40970:10:
      40970 │     throw new Error(`Failed to resolve entry for package "${id}". ` +
            ╵           ^

    at packageEntryFailure (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:40970:11)
    at resolvePackageEntry (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:40966:9)
    at tryResolveFile (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:40682:38)
    at tryFsResolve (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:40664:16)
    at Context.resolveId (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:40537:28)
    at Object.resolveId (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:39254:55)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async resolve (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:39466:26)
    at async /Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:39677:34
    at async callback (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/esbuild/lib/main.js:933:28)

  This error came from the "onResolve" callback registered here:

    node_modules/vite/dist/node/chunks/dep-689425f3.js:39673:18:
      39673 │             build.onResolve({
            ╵                   ~~~~~~~~~

    at setup (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:39673:19)
    at handlePlugins (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/esbuild/lib/main.js:855:23)
    at Object.buildOrServe (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/esbuild/lib/main.js:1149:7)
    at /Users/b/projects/playground/rqa/react-query-auth/example/node_modules/esbuild/lib/main.js:2110:17
    at new Promise (<anonymous>)
    at Object.build (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/esbuild/lib/main.js:2109:14)
    at Object.build (/Users/b/projects/playground/rqa/react-query-auth/example/node_modules/esbuild/lib/main.js:1956:51)
    at /Users/b/projects/playground/rqa/react-query-auth/example/node_modules/vite/dist/node/chunks/dep-689425f3.js:39414:54
    at Array.map (<anonymous>)

  The plugin "vite:dep-scan" was triggered by this import

    lib/auth.ts:1:30:
      1 │ import { configureAuth } from '../..';
        ╵                               ~~~~~~~

Build failed with 1 error:
node_modules/vite/dist/node/chunks/dep-689425f3.js:40970:10: ERROR: [plugin: vite:dep-scan] Failed to resolve entry for package "/Users/b/projects/playground/rqa/react-query-auth". The package may have incorrect main/module/exports specified in its package.json: Failed to resolve entry for package "/Users/b/projects/playground/rqa/react-query-auth". The package may have incorrect main/module/exports specified in its package.json.

I'm not sure if my Node environment is the culprit? If so, any pointers how to fix it would be very appreciated. Thanks!

After logged in when user Referesh the page useAuth dont have user info

Hi team,
Can anyone help me with useAuth user info?
After successfully logging in if the user refreshes browser user info from useAUth got null, it tried to fetch new info from API in that case user logged in, and the user can not stay logged in that case. though we already have token/ user info in our session storage it will return null.
I tied to access useAuth at top-level routes where I am rending routes based on user info if it null It will redirect to login page So basically I got logged out when I refresh the page. but the same user auth can have user info inside protected routes.

How to manage login errors

Hi ! Thanks for your package

I am wondering, how to pass the server response message all the way to the View using useAuth ?
I played with the axios interceptors but the login function would always succeed but even if I promise.reject in the loginFn, how to get the message to the useAuth props ?

Hope I was clear, it is a very generic issue to be honest.
Logging in wrong credentials, 401 error from the backend with a message -> how do I display the message?
Thanks !

Setting up default react-query options for loadUser() query

Hi @alan2207,

It would be great if react-query-auth provided some props for setting react-query options for this particular query loadUser(). The problem is, on every tab/window refocus abovementioned function is called and logically speaking that is not necessary. Shouldn' t be loadUser() called by the developer when user data is needed?

Unhandled status: loading (Not making sense errors)

Hi,

Unhandled status: error or Unhandled status: loading

async function LoginUser(auth: User) {
  const response = await http.post<AuthResponse>("Auth/login", auth);
  const { user, jwt } = response.data as AuthResponse;
  Storage.setToken(jwt);
  return user;
}

export const verifyEmailFn = async (verificationCode: string) => {
  const response = await http.get<GeneralResponse>(
    `Auth/verifyemail/${verificationCode}`
  );
  return response.data;
};

const LogOutUser = () => {
  const response = http
    .get<GeneralResponse>("Auth/logout")
    .then((res) => res.data);
  return response;
};

function GetUser() {
  const user = http.get<User>("users/me").then((res) => res.data);
  return user;
}

const RegisterUser = (credentials: RegisterCredentials) => {
  const response = http
    .post<User>("Auth/register", credentials as RegisterCredentials)
    .then((res) => res.data);
  return response;
};

export const { useUser, useLogin, useRegister, useLogout, AuthLoader } =
  configureAuth({
    userFn: () => GetUser(),
    logoutFn: () => LogOutUser(),
    registerFn: (credentials: RegisterCredentials) => RegisterUser(credentials),
    loginFn: (user: User) => LoginUser(user),
  });

Could this error make sense, at least pls ?

AuthProviderConfig logoutFn parameter should not be strictly empty

Is there a disadvantage of setting AuthProviderConfig like this:

export interface AuthProviderConfig<User = unknown, Error = unknown> {
    key?: string;
    loadUser: (data: any) => Promise<User>;
    loginFn: (data: any) => Promise<User>;
    registerFn: (data: any) => Promise<User>;
    logoutFn: (data?: any) => Promise<any>;
    waitInitial?: boolean;
    LoaderComponent?: () => JSX.Element;
    ErrorComponent?: ({ error }: {
        error: Error | null;
    }) => JSX.Element;
}

Otherwise I was not able to set the logoutFn with a parameter.

Original:

export interface AuthProviderConfig<User = unknown, Error = unknown> {
    key?: string;
    loadUser: (data: any) => Promise<User>;
    loginFn: (data: any) => Promise<User>;
    registerFn: (data: any) => Promise<User>;
    logoutFn: () => Promise<any>;
    waitInitial?: boolean;
    LoaderComponent?: () => JSX.Element;
    ErrorComponent?: ({ error }: {
        error: Error | null;
    }) => JSX.Element;
}

@alan2207 what are your thoughts on this? Should I open a pull request or is there something incorrect with this proposal?

showing a 401 errormessage from the server

When logging in with wrong login-details I would like to show the 401 error message from the server. How would this be possible? I am also a bit confused as the docs refer to an error that can be derived from the useAuth hook:

  const { user, login, logout, register, error, refetch } = useAuth();

But when I look at the codesandbox example it is not used, but a useState hook on the onSubmit catched error is used. Could you maybe explain how this should work?

Could not find a declaration file for module 'react-query-auth'

I am getting error while importing this library, I think there is problem with package.json in library

Screenshot 2023-06-21 110844 Screenshot 2023-06-21 110653

this is my package.json

{
  "name": "malma-up-todo",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "@headlessui/react": "^1.7.15",
    "@tanstack/react-query": "^4.29.15",
    "axios": "^1.4.0",
    "clsx": "^1.2.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-error-boundary": "^4.0.10",
    "react-loader-spinner": "^5.3.4",
    "react-query-auth": "^2.2.0",
    "zustand": "^4.3.8"
  },
  "devDependencies": {
    "@types/react": "^18.0.37",
    "@types/react-dom": "^18.0.11",
    "@typescript-eslint/eslint-plugin": "^5.59.0",
    "@typescript-eslint/parser": "^5.59.0",
    "@vitejs/plugin-react-swc": "^3.0.0",
    "autoprefixer": "^10.4.14",
    "eslint": "^8.38.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.3.4",
    "postcss": "^8.4.24",
    "tailwindcss": "^3.3.2",
    "typescript": "^5.0.2",
    "vite": "^4.3.9"
  }
}

set query-retries

My general queryClientConfig for react-query is more or less the default one:

queryClientConfig: {
        defaultOptions: {
            queries: {
                retry: 2,
                refetchOnMount: "always",
                refetchOnWindowFocus: "always",
                refetchOnReconnect: "always",
                cacheTime: 1000*30,
                refetchInterval: false,
                refetchIntervalInBackground: false,
                suspense: false,
                staleTime: 1000 * 30,
            },
            mutations: {
                retry: 2,
            },
        }
    },

When I login with wrong login-details I get a 401, however I get three 401's (mutations are retried twice). When I reconfigure and set retry to 0 it indeed works as I want.

You can also set retry config per request:
https://react-query.tanstack.com/guides/query-retries

So I would like to set number of retries only for login, but how would I do that using react-query-auth?

Not working with React Native

As soon as you try to use the AuthProvider, the followin error appears:

ExceptionsManager.js:149 Error: Text strings must be rendered within a component.

Thank you.

Dear maintainers, thank you for creating this package. Takes care of all the user context stuff. 😄

refetchUser function example/ invalidate auth-user

Hi,

Can you please provide an example of how to force a user refetch?
The docs mention a refetchUser function but that is not used anywhere in the demo.
I've tried invalidating the query key after a mutation but that doesn't force a refetch either.

Thanks!

Refresh token

Is there a specific function where I am supposed to put the refreshing users auth tokens logic? Following the doc doesnt seem to mention it.

renderError not working with v.2.2.0

hello @alan2207

renderError does not work on vite example.
How should I implement this?

First, update the version of react-query-auth to v.2.2.0.

yarn remove react-query-auth
yarn add react-query-auth

examples/vite/package.json

  "dependencies": {
    "@tanstack/react-query": "^4.24.6",
    "@tanstack/react-query-devtools": "^4.24.6",
    "cross-env": "^7.0.3",
    "msw": "^1.0.1",
    "react-app-polyfill": "^3.0.0",
    "react-query-auth": "^2.2.0"
  },
  "devDependencies": {
    "@types/node": "^18.14.0",
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^4",
    "vite": "latest",
    "vite-preset-react": "latest"
  },

Add renderError.

examples/vite/src/App.tsx

const SampleApp = () => {
  const [queryClient] = React.useState(() => new QueryClient());

  return (
    <Container>
      <QueryClientProvider client={queryClient}>
        <AuthLoader
          renderLoading={() => <div>Loading ...</div>}
          renderUnauthenticated={() => <AuthScreen />}
+          renderError={(error) => <AuthScreen />}
        >
          <UserInfo />
        </AuthLoader>
        <ReactQueryDevtools initialIsOpen={false} />
      </QueryClientProvider>
    </Container>
  );
};

A warning is displayed.

image

type '{ children: renderLoading: () => Element; renderUnauthenticated: () => Element; renderError: (error: any) => Element; }' type 'IntrinsicAttributes & { children: ReactNode; renderLoading: () => Element; renderUnauthenticated?: (() => Element). | cannot be assigned.
  Property 'renderError' is of type 'IntrinsicAttributes & { children: ReactNode; renderLoading: () => Element; renderUnauthenticated?: (() => Element) | Undefined; }' The following is not present in the

Translated with www.DeepL.com/Translator (free version)

Is there a problem with renderError not being present in index.d.ts in node_modules?

examples/vite/node_modules/react-query-auth/dist/index.d.ts

declare function configureAuth<User, Error, LoginCredentials, RegisterCredentials>(config: ReactQueryAuthConfig<User, LoginCredentials, RegisterCredentials>): {
    useUser: (options?: Omit<UseQueryOptions<User, Error, User, QueryKey>, 'queryKey' | 'queryFn'>) => _tanstack_react_query.UseQueryResult<User, Error>;
    useLogin: (options?: Omit<UseMutationOptions<User, Error, LoginCredentials>, 'mutationFn'>) => _tanstack_react_query.UseMutationResult<User, Error, LoginCredentials, unknown>;
    useRegister: (options?: Omit<UseMutationOptions<User, Error, RegisterCredentials>, 'mutationFn'>) => _tanstack_react_query.UseMutationResult<User, Error, RegisterCredentials, unknown>;
    useLogout: (options?: UseMutationOptions<unknown, Error, unknown>) => _tanstack_react_query.UseMutationResult<unknown, Error, unknown, unknown>;
    AuthLoader: ({ children, renderLoading, renderUnauthenticated, }: {
        children: React.ReactNode;
        renderLoading: () => JSX.Element;
        renderUnauthenticated?: (() => JSX.Element) | undefined;
    }) => JSX.Element;
};

How can the problem be resolved?

Uncaught Error: useAuth must be used within an AuthProvider

Hi @alan2207,

Sometimes (I can't tell specifically when) I get this error even though useAuth is used inside AuthProvider. Because this is uncaught error, React unmounts the whole tree (and I'm not using useErrorBoundary on react-query client). What is/might be the reason for this behaviour?

Thanks

[Nextjs] Issue with 2.1.1

hey @alan2207, I recently installed the updated version of react-query-auth 2.1.1 and I am getting an error

cannot find module: 'react/jsx-runtime'
did you mean react/jsx-runtime.js

image

cleanup function

I am using code very similar to this codepen, but when I login using an axios call I get this warning:

index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
    at Login (http://localhost:3000/static/js/main.chunk.js:2036:67)
    at Route (http://localhost:3000/static/js/vendors~main.chunk.js:55437:29)

Probably I should cleanup my axios call somehow, but I do not really have clue how to do that?! Tried, but no success so far. I think the useEffect must be in the library as it is not in my code?!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.