Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions packages/atoms/src/classes/Signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,11 @@ export const doMutate = <G extends NodeGenerics>(

const transactions: Transaction[] = []

if (isMutatable(newState)) {
if (Array.isArray(newState)) {
transactions.push({ k: [], v: newState })
} else if (isMutatable(newState)) {
const empty = (
newState instanceof Set
? new Set()
: Array.isArray(newState)
? []
: {}
newState instanceof Set ? new Set() : {}
) as G['State']

recursivelyMutate(
Expand Down Expand Up @@ -138,7 +136,10 @@ export const doMutate = <G extends NodeGenerics>(
// applied as mutations; non-mutatable values (primitives, class instances,
// etc.) are set as the entire new state.
if (!transactions.length && result !== undefined) {
if (isMutatable(result)) {
if (Array.isArray(result)) {
newState = result
transactions.push({ k: [], v: result })
} else if (isMutatable(result)) {
recursivelyMutate(proxyWrapper.p, result)
} else {
node.set(result as Settable<G['State']>, {
Expand All @@ -149,6 +150,9 @@ export const doMutate = <G extends NodeGenerics>(
return
}
}
} else if (Array.isArray(mutatable)) {
newState = mutatable as G['State']
transactions.push({ k: [], v: mutatable })
} else {
recursivelyMutate(proxyWrapper.p, mutatable)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ exports[`proxies array operations 1`] = `
[
[
{
"k": [
"0",
"a",
],
"k": [],
"v": [
11,
33,
{
"a": [
11,
33,
],
},
],
},
],
Expand Down
63 changes: 61 additions & 2 deletions packages/react/test/integrations/proxies.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { api, As, atom, injectSignal, Transaction } from '@zedux/atoms'
import {
api,
As,
atom,
injectMappedSignal,
injectSignal,
Transaction,
} from '@zedux/atoms'
import { ecosystem } from '../utils/ecosystem'

describe('proxies', () => {
Expand Down Expand Up @@ -317,7 +324,21 @@ describe('proxies', () => {
state[3] = 40
})

expect(signal.get()).toEqual([2, 3, 30, 40, 5])
expect(signal.get()).toEqual([2, 3, 30, 40])
})

test('function-form returning an array generates a single transaction', () => {
const calls: any[] = []
const signal = ecosystem.signal([1, 2, 3])

signal.on('mutate', transactions => {
calls.push(transactions)
})

signal.mutate(draft => [draft[0] + 10, draft[1] + 10])

expect(signal.get()).toEqual([11, 12])
expect(calls).toEqual([[{ k: [], v: [11, 12] }]])
})

describe('recursivelyMutate optimizations', () => {
Expand Down Expand Up @@ -382,6 +403,44 @@ describe('proxies', () => {
expect(signal.get()).toEqual({ arr: [11, 21] })
expect(calls).toEqual([[{ k: 'arr', v: [11, 21] }]])
})

test('top-level array replaces instead of deep merging', () => {
const calls: any[] = []
const signal = ecosystem.signal([{ a: 1, b: 2 }, { c: 3 }])

signal.on('mutate', transactions => {
calls.push(transactions)
})

signal.mutate([{ a: 5 }])

// should replace entire array, not deep merge { a: 5 } into { a: 1, b: 2 }
expect(signal.get()).toEqual([{ a: 5 }])
expect(calls).toEqual([[{ k: [], v: [{ a: 5 }] }]])
})
})

test('mapped signal prefixes array replacement transaction with inner key', () => {
const testAtom = atom('mappedArr', () => {
const arrSignal = injectSignal([1, 2, 3])
const signal = injectMappedSignal({ arr: arrSignal })

return api(signal).setExports({ arrSignal })
})

const node = ecosystem.getNode(testAtom)
const calls: any[] = []

node.on('mutate', transactions => {
calls.push(transactions)
})

node.exports.arrSignal.mutate([10, 20])

expect(node.get()).toEqual({ arr: [10, 20] })
expect(calls).toEqual([[{ k: ['arr'], v: [10, 20] }]])

ecosystem.dehydrate({ exclude: [testAtom] })
})

describe('non-plain object handling', () => {
Expand Down
8 changes: 3 additions & 5 deletions packages/react/test/integrations/signals.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ describe('signals', () => {
])
})

test('mutate generates add transactions for each element of an array', () => {
test('mutate replaces array state directly with a single transaction', () => {
const signal = ecosystem.signal<string[] | null>(null)
let transactions: Transaction[] | undefined

Expand All @@ -538,10 +538,8 @@ describe('signals', () => {

signal.mutate(['a', 'b'])

expect(transactions).toEqual([
{ k: '0', v: 'a' },
{ k: '1', v: 'b' },
])
expect(transactions).toEqual([{ k: [], v: ['a', 'b'] }])
expect(signal.get()).toEqual(['a', 'b'])
})

test('mutate generates add transactions for each item in a set', () => {
Expand Down
Loading