Skip to content

Commit

Permalink
enhance: Schemas with no entities should not infer (#323)
Browse files Browse the repository at this point in the history
  • Loading branch information
ntucker committed Apr 26, 2020
1 parent 88d5627 commit 79f048d
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 3 deletions.
Expand Up @@ -391,7 +391,7 @@ describe('useResource()', () => {
const { result, waitForNextUpdate } = renderRestHook(() => {
return useResource(photoShape, { userId });
});
expect(result.current).toBe(null);
expect(result.current).toBe(undefined);
await waitForNextUpdate();
expect(result.current).toStrictEqual(response);
});
Expand Down
Expand Up @@ -4,6 +4,7 @@ import {
NestedArticleResource,
UserResource,
IndexedUserResource,
photoShape,
} from '__tests__/common';
import { normalize, NormalizedIndex } from 'rest-hooks/resource';
import { initialState } from 'rest-hooks/state/reducer';
Expand Down Expand Up @@ -164,7 +165,7 @@ describe('useDenormalized()', () => {
});
});

describe.only('no result exists but index is used when using nested schema', () => {
describe('no result exists but index is used when using nested schema', () => {
const pageArticle = PaginatedArticleResource.fromJS({
...params,
author: 23,
Expand Down Expand Up @@ -590,6 +591,30 @@ describe('useDenormalized()', () => {
});
});

it('should not infer with schemas that have no entities', () => {
const userId = '5';
const { result } = renderHook(() => {
return useDenormalized(photoShape, { userId }, initialState as any);
});
expect(result.current).toStrictEqual([undefined, true]);
});

it('should return results as-is for schemas with no entities', () => {
const userId = '5';
const results = new ArrayBuffer(10);
const state = {
...initialState,
results: {
...initialState.results,
[photoShape.getFetchKey({ userId })]: results,
},
};
const { result } = renderHook(() => {
return useDenormalized(photoShape, { userId }, state);
});
expect(result.current).toStrictEqual([results, true]);
});

it('should throw with invalid schemas', () => {
const shape = PaginatedArticleResource.listShape();
shape.schema = { happy: { go: { lucky: 5 } } } as any;
Expand Down
27 changes: 26 additions & 1 deletion packages/rest-hooks/src/state/selectors/useDenormalized.ts
Expand Up @@ -5,7 +5,7 @@ import {
DenormalizeNullable,
ParamsFromShape,
} from 'rest-hooks/resource';
import { isEntity } from '@rest-hooks/normalizr';
import { isEntity, Schema } from '@rest-hooks/normalizr';
import { useMemo } from 'react';

import hasUsableData from '../../react-integration/hooks/hasUsableData';
Expand Down Expand Up @@ -47,8 +47,16 @@ export default function useDenormalized<
}, [cacheResults, state.indexes, serializedParams]);
// TODO: only update when relevant indexes change

const needsDenormalization = useMemo(() => schemaHasEntity(schema), [schema]);

// Compute denormalized value
const [denormalized, entitiesFound, entitiesList] = useMemo(() => {
if (!needsDenormalization)
return [cacheResults, true, ''] as [
DenormalizeNullable<Shape['schema']>,
any,
string,
];
// Warn users with bad configurations
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production' && isEntity(schema)) {
Expand Down Expand Up @@ -98,6 +106,7 @@ export default function useDenormalized<
serializedParams,
results,
expired,
needsDenormalization,
options && options.invalidIfStale,
]);

Expand All @@ -108,3 +117,19 @@ export default function useDenormalized<
entitiesList,
]);
}

/** Determine whether the schema has any entities.
*
* Without entities, denormalization is not needed, and results should not be inferred.
*/
function schemaHasEntity(schema: Schema): boolean {
if (isEntity(schema)) return true;
if (Array.isArray(schema) && schema.length) return schemaHasEntity(schema[0]);
if (schema && typeof schema === 'object' && !('denormalize' in schema)) {
return Object.values(schema).reduce(
(prev, cur) => prev || schemaHasEntity(cur),
false,
);
}
return false;
}

0 comments on commit 79f048d

Please sign in to comment.