mirror of
https://github.com/streamich/react-use.git
synced 2026-01-18 14:06:52 +00:00
feat: 🎸 add useUpsert
This commit is contained in:
parent
797cdea7eb
commit
6875e13d60
74
src/__tests__/useUpsert.test.ts
Normal file
74
src/__tests__/useUpsert.test.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import useUpsert from '../useUpsert';
|
||||
|
||||
interface TestItem {
|
||||
id: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const testItems: TestItem[] = [{ id: '1', text: '1' }, { id: '2', text: '2' }];
|
||||
|
||||
const itemsAreEqual = (a: TestItem, b: TestItem) => {
|
||||
return a.id === b.id;
|
||||
};
|
||||
|
||||
const setUp = (initialList: TestItem[] = []) => renderHook(() => useUpsert<TestItem>(itemsAreEqual, initialList));
|
||||
|
||||
describe('useUpsert', () => {
|
||||
describe('initialization', () => {
|
||||
const { result } = setUp(testItems);
|
||||
const [list, utils] = result.current;
|
||||
|
||||
it('properly initiates the list content', () => {
|
||||
expect(list).toEqual(testItems);
|
||||
});
|
||||
|
||||
it('returns an upsert function', () => {
|
||||
expect(utils.upsert).toBeInstanceOf(Function);
|
||||
});
|
||||
});
|
||||
|
||||
describe('upserting a new item', () => {
|
||||
const { result } = setUp(testItems);
|
||||
const [, utils] = result.current;
|
||||
|
||||
const newItem: TestItem = {
|
||||
id: '3',
|
||||
text: '3',
|
||||
};
|
||||
act(() => {
|
||||
utils.upsert(newItem);
|
||||
});
|
||||
|
||||
it('inserts a new item', () => {
|
||||
expect(result.current[0]).toContain(newItem);
|
||||
});
|
||||
it('works immutably', () => {
|
||||
expect(result.current[0]).not.toBe(testItems);
|
||||
});
|
||||
});
|
||||
|
||||
describe('upserting an existing item', () => {
|
||||
const { result } = setUp(testItems);
|
||||
const [, utils] = result.current;
|
||||
|
||||
const newItem: TestItem = {
|
||||
id: '2',
|
||||
text: '4',
|
||||
};
|
||||
act(() => {
|
||||
utils.upsert(newItem);
|
||||
});
|
||||
const updatedList = result.current[0];
|
||||
|
||||
it('has the same length', () => {
|
||||
expect(updatedList).toHaveLength(testItems.length);
|
||||
});
|
||||
it('updates the item', () => {
|
||||
expect(updatedList).toContain(newItem);
|
||||
});
|
||||
it('works immutably', () => {
|
||||
expect(updatedList).not.toBe(testItems);
|
||||
});
|
||||
});
|
||||
});
|
||||
37
src/useUpsert.ts
Normal file
37
src/useUpsert.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import useList, { Actions as ListActions } from './useList';
|
||||
|
||||
export interface Actions<T> extends ListActions<T> {
|
||||
upsert: (item: T) => void;
|
||||
}
|
||||
|
||||
const useUpsert = <T>(
|
||||
itemsAreTheSame: (upsertedItem: T, existingItem: T) => boolean,
|
||||
initialList: T[] = []
|
||||
): [T[], Actions<T>] => {
|
||||
const [items, actions] = useList(initialList);
|
||||
|
||||
const upsert = (upsertedItem: T) => {
|
||||
const itemAlreadyExists = items.find(item => itemsAreTheSame(upsertedItem, item));
|
||||
if (itemAlreadyExists) {
|
||||
return actions.set(
|
||||
items.map(existingItem => {
|
||||
if (itemsAreTheSame(upsertedItem, existingItem)) {
|
||||
return upsertedItem;
|
||||
}
|
||||
return existingItem;
|
||||
})
|
||||
);
|
||||
}
|
||||
return actions.push(upsertedItem);
|
||||
};
|
||||
|
||||
return [
|
||||
items,
|
||||
{
|
||||
...actions,
|
||||
upsert,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export default useUpsert;
|
||||
Loading…
x
Reference in New Issue
Block a user