diff --git a/packages/ra-core/src/auth/WithPermissions.tsx b/packages/ra-core/src/auth/WithPermissions.tsx index 674c0286f2e..a19cb38f6d6 100644 --- a/packages/ra-core/src/auth/WithPermissions.tsx +++ b/packages/ra-core/src/auth/WithPermissions.tsx @@ -3,7 +3,7 @@ import { Location } from 'react-router-dom'; import warning from '../util/warning'; import { useAuthenticated } from './useAuthenticated'; -import usePermissionsOptimized from './usePermissionsOptimized'; +import usePermissions from './usePermissions'; export interface WithPermissionsChildrenParams { permissions: any; @@ -76,7 +76,7 @@ const WithPermissions = (props: WithPermissionsProps) => { ); useAuthenticated(authParams); - const { permissions } = usePermissionsOptimized(authParams); + const { permissions } = usePermissions(authParams); // render even though the usePermissions() call isn't finished (optimistic rendering) if (component) { return createElement(component, { permissions, ...rest }); diff --git a/packages/ra-core/src/auth/usePermissionsOptimized.spec.tsx b/packages/ra-core/src/auth/usePermissionsOptimized.spec.tsx deleted file mode 100644 index ef38304c53c..00000000000 --- a/packages/ra-core/src/auth/usePermissionsOptimized.spec.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import * as React from 'react'; -import { render, act, cleanup } from '@testing-library/react'; -import expect from 'expect'; -import usePermissionsOptimized from './usePermissionsOptimized'; -import AuthContext from './AuthContext'; - -describe('usePermissionsOptimized', () => { - afterEach(cleanup); - - const CallPermissionsOnMount = ({ number, authParams }: any) => { - const { permissions } = usePermissionsOptimized(authParams); - return ( -
- permissions {number}: {permissions} -
- ); - }; - - it('returns undefined on mount', () => { - const getPermissions = jest.fn(() => Promise.resolve('admin')); - const { queryByText } = render( - -
- -
-
- ); - expect(queryByText('permissions :')).not.toBeNull(); - expect(queryByText('permissions : admin')).toBeNull(); - }); - - it('returns permissions from authProvider after resolve', async () => { - const getPermissions = jest.fn(() => Promise.resolve('admin')); - const { queryByText } = render( - -
- -
-
- ); - await act(async () => await new Promise(r => setTimeout(r))); - expect(queryByText('permissions :')).toBeNull(); - expect(queryByText('permissions : admin')).not.toBeNull(); - }); - - it('does not rerender once the permissions have already been fetched', async () => { - let renders = 0; - const ComponentToTest = () => { - const { permissions } = usePermissionsOptimized({ test: 3 }); - renders++; - return
{permissions}
; - }; - const getPermissions = jest.fn(() => Promise.resolve('admin')); - - // first usage - const { queryByText } = render( - - - - ); - expect(renders).toBe(1); // renders on mount - expect(getPermissions).toBeCalledTimes(1); - expect(queryByText('admin')).toBeNull(); - await act(async () => await new Promise(r => setTimeout(r))); - expect(renders).toBe(2); // re-renders when the getPermissions returns - expect(queryByText('admin')).not.toBeNull(); - - // second usage - cleanup(); - renders = 0; - const { queryByText: queryByText2 } = render( - - - - ); - expect(renders).toBe(1); // renders on mount - expect(getPermissions).toBeCalledTimes(2); - expect(queryByText2('admin')).not.toBeNull(); // answer from the cache - await act(async () => await new Promise(r => setTimeout(r))); - expect(renders).toBe(1); // does not rerender when the getPermissions returns the same permissions - }); - - it('can be called by two independent components', async () => { - const getPermissions = jest.fn(() => Promise.resolve('admin')); - const { queryByText } = render( - -
- - -
-
- ); - expect(queryByText('permissions 1:')).not.toBeNull(); - expect(queryByText('permissions 2:')).not.toBeNull(); - expect(queryByText('permissions 1: admin')).toBeNull(); - expect(queryByText('permissions 2: admin')).toBeNull(); - expect(getPermissions).toBeCalledTimes(2); - await act(async () => await new Promise(r => setTimeout(r))); - expect(queryByText('permissions 1:')).toBeNull(); - expect(queryByText('permissions 2:')).toBeNull(); - expect(queryByText('permissions 1: admin')).not.toBeNull(); - expect(queryByText('permissions 2: admin')).not.toBeNull(); - expect(getPermissions).toBeCalledTimes(2); - }); -}); diff --git a/packages/ra-core/src/auth/usePermissionsOptimized.ts b/packages/ra-core/src/auth/usePermissionsOptimized.ts index 74681e2ddbd..fe932d94c00 100644 --- a/packages/ra-core/src/auth/usePermissionsOptimized.ts +++ b/packages/ra-core/src/auth/usePermissionsOptimized.ts @@ -1,79 +1,14 @@ -import { useEffect } from 'react'; -import isEqual from 'lodash/isEqual'; - -import useGetPermissions from './useGetPermissions'; -import { useSafeSetState } from '../util/hooks'; - -interface State { - permissions?: any; - error?: any; -} +import usePermissions from './usePermissions'; const emptyParams = {}; -// keep a cache of already fetched permissions to initialize state for new -// components and avoid a useless rerender if the permissions haven't changed -const alreadyFetchedPermissions = { '{}': undefined }; - /** - * Hook for getting user permissions without the loading state. - * - * When compared to usePermissions, this hook doesn't cause a re-render - * when the permissions haven't changed since the last call. - * - * This hook doesn't handle the loading state. + * @deprecated use usePermissions instead * * @see usePermissions - * - * Calls the authProvider.getPermissions() method asynchronously. - * If the authProvider returns a rejected promise, returns empty permissions. - * - * The return value updates according to the request state: - * - * - start: { permissions: [previously fetched permissions for these params] } - * - success: { permissions: [permissions returned by the authProvider (usually the same as on start)] } - * - error: { error: [error from provider] } - * - * Useful to enable features based on user permissions - * - * @param {Object} params Any params you want to pass to the authProvider - * - * @returns The current auth check state. Destructure as { permissions, error }. - * - * @example - * import { usePermissionsOptimized } from 'react-admin'; - * - * const PostDetail = props => { - * const { permissions } = usePermissionsOptimized(); - * if (permissions !== 'editor') { - * return - * } else { - * return - * } - * }; */ const usePermissionsOptimized = (params = emptyParams) => { - const key = JSON.stringify(params); - const [state, setState] = useSafeSetState({ - permissions: alreadyFetchedPermissions[key], - }); - const getPermissions = useGetPermissions(); - useEffect(() => { - getPermissions(params) - .then(permissions => { - if (!isEqual(permissions, state.permissions)) { - alreadyFetchedPermissions[key] = permissions; - setState({ permissions }); - } - }) - .catch(error => { - setState({ - error, - }); - }); - }, [getPermissions, key]); // eslint-disable-line react-hooks/exhaustive-deps - - return state; + return usePermissions(params); }; export default usePermissionsOptimized;