UNPKG

@reduxjs/toolkit

Version:

The official, opinionated, batteries-included toolset for efficient Redux development

156 lines (126 loc) 4.17 kB
import { EntityState, EntityStateAdapter, IdSelector, Update, EntityId } from './models' import { createStateOperator } from './state_adapter' import { selectIdValue } from './utils' export function createUnsortedStateAdapter<T>( selectId: IdSelector<T> ): EntityStateAdapter<T> { type R = EntityState<T> function addOneMutably(entity: T, state: EntityState<T>): void { const key = selectIdValue(entity, selectId) if (key in state.entities) { return } state.ids.push(key) state.entities[key] = entity } function addManyMutably(entities: T[], state: R): void { for (const entity of entities) { addOneMutably(entity, state) } } function setAllMutably(entities: T[], state: R): void { state.ids = [] state.entities = {} addManyMutably(entities, state) } function removeOneMutably(key: EntityId, state: R): void { return removeManyMutably([key], state) } function removeManyMutably(keys: EntityId[], state: R): void { let didMutate = false keys.forEach(key => { if (key in state.entities) { delete state.entities[key] didMutate = true } }) if (didMutate) { state.ids = state.ids.filter(id => id in state.entities) } } function removeAll(state: R): any { return Object.assign({}, state, { ids: [], entities: {} }) } function takeNewKey( keys: { [id: string]: EntityId }, update: Update<T>, state: R ): boolean { const original = state.entities[update.id] const updated: T = Object.assign({}, original, update.changes) const newKey = selectIdValue(updated, selectId) const hasNewKey = newKey !== update.id if (hasNewKey) { keys[update.id] = newKey delete state.entities[update.id] } state.entities[newKey] = updated return hasNewKey } function updateOneMutably(update: Update<T>, state: R): void { return updateManyMutably([update], state) } function updateManyMutably(updates: Update<T>[], state: R): void { const newKeys: { [id: string]: EntityId } = {} const updatesPerEntity: { [id: string]: Update<T> } = {} updates.forEach(update => { // Only apply updates to entities that currently exist if (update.id in state.entities) { // If there are multiple updates to one entity, merge them together updatesPerEntity[update.id] = { // Spreads ignore falsy values, so this works even if there isn't // an existing update already at this key ...updatesPerEntity[update.id], ...update } } }) updates = Object.values(updatesPerEntity) const didMutateEntities = updates.length > 0 if (didMutateEntities) { const didMutateIds = updates.filter(update => takeNewKey(newKeys, update, state)).length > 0 if (didMutateIds) { state.ids = state.ids.map(id => newKeys[id] || id) } } } function upsertOneMutably(entity: T, state: R): void { return upsertManyMutably([entity], state) } function upsertManyMutably(entities: T[], state: R): void { const added: T[] = [] const updated: Update<T>[] = [] for (const entity of entities) { const id = selectIdValue(entity, selectId) if (id in state.entities) { updated.push({ id, changes: entity }) } else { added.push(entity) } } updateManyMutably(updated, state) addManyMutably(added, state) } return { removeAll, addOne: createStateOperator(addOneMutably), addMany: createStateOperator(addManyMutably), setAll: createStateOperator(setAllMutably), updateOne: createStateOperator(updateOneMutably), updateMany: createStateOperator(updateManyMutably), upsertOne: createStateOperator(upsertOneMutably), upsertMany: createStateOperator(upsertManyMutably), removeOne: createStateOperator(removeOneMutably), removeMany: createStateOperator(removeManyMutably) } }