Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[@xstate/store] v3 #5175

Merged
merged 26 commits into from
Feb 9, 2025
Merged
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
49cf959
Only complete assigners are allowed
davidkpiano Jan 16, 2025
a0b5dd2
`createStore` only takes a single { context, on } argument now
davidkpiano Jan 19, 2025
4f3d224
createStoreWithProducer
davidkpiano Jan 19, 2025
4d2ddf0
Enhance store functionality by introducing effects in event handling
davidkpiano Jan 22, 2025
d55e963
use overload trick 🫠
Andarist Jan 22, 2025
73c9489
Fix fromStore
davidkpiano Jan 23, 2025
3d2cded
Update changesets
davidkpiano Jan 25, 2025
cfe28e6
Add support for type parameters
davidkpiano Jan 25, 2025
c0efebf
Revert "Add support for type parameters"
davidkpiano Jan 26, 2025
8528460
Refactor store type parameters to improve type safety and flexibility
davidkpiano Jan 26, 2025
008badf
Add trigger + test + changeset
davidkpiano Jan 26, 2025
370796e
Merge branch 'main' into davidkpiano/store-v3
davidkpiano Jan 30, 2025
ad23c06
Add emits
davidkpiano Jan 31, 2025
babb74d
Fixed TS issue
Andarist Jan 31, 2025
610ca15
use correct types
Andarist Jan 31, 2025
80fe593
Remove changeset
davidkpiano Feb 1, 2025
b325d68
use util
Andarist Feb 2, 2025
0433e3d
remove redundant test
Andarist Feb 2, 2025
0e53d88
tweak things
Andarist Feb 2, 2025
0ad2815
remove commented out code
Andarist Feb 2, 2025
cb03154
Quick typestates test
davidkpiano Feb 3, 2025
3672932
Update tests
davidkpiano Feb 8, 2025
5935e0c
Update fromStore
davidkpiano Feb 8, 2025
e6c2523
Fix tests
davidkpiano Feb 8, 2025
2276832
Update packages/xstate-store/test/fromStore.test.ts
davidkpiano Feb 9, 2025
c2ea8e3
Improve jsdoc comments and test
davidkpiano Feb 9, 2025
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
Next Next commit
Only complete assigners are allowed
  • Loading branch information
davidkpiano committed Jan 16, 2025
commit 49cf95924cc58f1631bd4d6f34fb381031e07e5a
5 changes: 5 additions & 0 deletions .changeset/thick-paws-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@xstate/store': major
---

Only complete assigners can now be used
90 changes: 32 additions & 58 deletions packages/xstate-store/src/store.ts
Original file line number Diff line number Diff line change
@@ -9,11 +9,8 @@ import {
Recipe,
Store,
StoreAssigner,
StoreCompleteAssigner,
StoreContext,
StoreInspectionEvent,
StorePartialAssigner,
StorePropertyAssigner,
StoreSnapshot
} from './types';

@@ -57,17 +54,11 @@ function createStoreCore<
>(
initialContext: TContext,
transitions: {
[K in keyof TEventPayloadMap & string]:
| StoreAssigner<
NoInfer<TContext>,
{ type: K } & TEventPayloadMap[K],
TEmitted
>
| StorePropertyAssigner<
NoInfer<TContext>,
{ type: K } & TEventPayloadMap[K],
TEmitted
>;
[K in keyof TEventPayloadMap & string]: StoreAssigner<
NoInfer<TContext>,
{ type: K } & TEventPayloadMap[K],
TEmitted
>;
},
updater?: (
context: NoInfer<TContext>,
@@ -207,21 +198,13 @@ export type TransitionsFromEventPayloadMap<
TContext extends StoreContext,
TEmitted extends EventObject
> = {
[K in keyof TEventPayloadMap & string]:
| StoreAssigner<
TContext,
{
type: K;
} & TEventPayloadMap[K],
TEmitted
>
| StorePropertyAssigner<
TContext,
{
type: K;
} & TEventPayloadMap[K],
TEmitted
>;
[K in keyof TEventPayloadMap & string]: StoreAssigner<
TContext,
{
type: K;
} & TEventPayloadMap[K],
TEmitted
>;
};

/**
@@ -264,17 +247,11 @@ export function createStore<
}: {
context: TContext;
on: {
[K in keyof TEventPayloadMap & string]:
| StoreAssigner<
NoInfer<TContext>,
{ type: K } & TEventPayloadMap[K],
Cast<TTypes['emitted'], EventObject>
>
| StorePropertyAssigner<
NoInfer<TContext>,
{ type: K } & TEventPayloadMap[K],
Cast<TTypes['emitted'], EventObject>
>;
[K in keyof TEventPayloadMap & string]: StoreAssigner<
NoInfer<TContext>,
{ type: K } & TEventPayloadMap[K],
Cast<TTypes['emitted'], EventObject>
>;
};
} & { types?: TTypes }): Store<
TContext,
@@ -445,13 +422,11 @@ export function createStoreTransition<
TEmitted extends EventObject
>(
transitions: {
[K in keyof TEventPayloadMap & string]:
| StoreAssigner<TContext, { type: K } & TEventPayloadMap[K], TEmitted>
| StorePropertyAssigner<
TContext,
{ type: K } & TEventPayloadMap[K],
TEmitted
>;
[K in keyof TEventPayloadMap & string]: StoreAssigner<
TContext,
{ type: K } & TEventPayloadMap[K],
TEmitted
>;
},
updater?: (
context: TContext,
@@ -480,9 +455,11 @@ export function createStoreTransition<
if (typeof assigner === 'function') {
currentContext = updater
? updater(currentContext, (draftContext) =>
(
assigner as StoreCompleteAssigner<TContext, StoreEvent, TEmitted>
)?.(draftContext, event, enqueue)
(assigner as StoreAssigner<TContext, StoreEvent, TEmitted>)?.(
draftContext,
event,
enqueue
)
)
: setter(currentContext, (draftContext) =>
Object.assign(
@@ -501,14 +478,11 @@ export function createStoreTransition<
const propAssignment = assigner[key];
partialUpdate[key] =
typeof propAssignment === 'function'
? (
propAssignment as StorePartialAssigner<
TContext,
StoreEvent,
typeof key,
TEmitted
>
)(currentContext, event, enqueue)
? (propAssignment as StoreAssigner<TContext, StoreEvent, TEmitted>)(
currentContext,
event,
enqueue
)
: propAssignment;
}
currentContext = Object.assign({}, currentContext, partialUpdate);
2 changes: 1 addition & 1 deletion packages/xstate-store/src/types.ts
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ export type StoreAssigner<
context: TContext,
event: TEvent,
enq: EnqueueObject<TEmitted>
) => Partial<TContext>;
) => TContext;
export type StoreCompleteAssigner<
TContext,
TEvent extends EventObject,
9 changes: 4 additions & 5 deletions packages/xstate-store/test/fromStore.test.ts
Original file line number Diff line number Diff line change
@@ -4,11 +4,10 @@ import { fromStore } from '../src/index.ts';
describe('fromStore', () => {
it('creates an actor from store logic with input (2 args)', () => {
const storeLogic = fromStore((count: number) => ({ count }), {
inc: {
count: (ctx, ev: { by: number }) => {
return ctx.count + ev.by;
}
}
inc: (ctx, ev: { by: number }) => ({
...ctx,
count: ctx.count + ev.by
})
});

const actor = createActor(storeLogic, {
49 changes: 28 additions & 21 deletions packages/xstate-store/test/react.test.tsx
Original file line number Diff line number Diff line change
@@ -14,9 +14,10 @@ it('useSelector should work', () => {
count: 0
},
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
...ctx,
count: ctx.count + 1
})
}
);

@@ -52,12 +53,14 @@ it('useSelector can take in a custom comparator', () => {
items: [1, 2]
},
{
same: {
items: () => [1, 2] // different array, same items
},
different: {
items: () => [3, 4]
}
same: (ctx) => ({
...ctx,
items: [1, 2] // different array, same items
}),
different: (ctx) => ({
...ctx,
items: [3, 4]
})
}
);

@@ -117,9 +120,10 @@ it('can batch updates', () => {
count: 0
},
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
...ctx,
count: ctx.count + 1
})
}
);

@@ -158,9 +162,10 @@ it('useSelector (@xstate/react) should work', () => {
count: 0
},
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
...ctx,
count: ctx.count + 1
})
}
);

@@ -196,9 +201,10 @@ it('useActor (@xstate/react) should work', () => {
count: 0
},
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
...ctx,
count: ctx.count + 1
})
}
);

@@ -234,9 +240,10 @@ it('useActorRef (@xstate/react) should work', () => {
count: 0
},
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
...ctx,
count: ctx.count + 1
})
}
);

20 changes: 16 additions & 4 deletions packages/xstate-store/test/solid.test.tsx
Original file line number Diff line number Diff line change
@@ -22,8 +22,14 @@ const createCounterStore = () =>
createStore(
{ count: 0, other: 0 },
{
increment: { count: ({ count }) => count + 1 },
other: { other: ({ other }) => other + 1 }
increment: (ctx) => ({
...ctx,
count: ctx.count + 1
}),
other: (ctx) => ({
...ctx,
other: ctx.other + 1
})
}
);

@@ -75,8 +81,14 @@ describe('Solid.js integration', () => {
const store = createStore(
{ items: INITIAL_ITEMS },
{
same: { items: () => [...INITIAL_ITEMS] },
different: { items: () => DIFFERENT_ITEMS }
same: (ctx) => ({
...ctx,
items: [...INITIAL_ITEMS]
}),
different: (ctx) => ({
...ctx,
items: DIFFERENT_ITEMS
})
}
);

33 changes: 17 additions & 16 deletions packages/xstate-store/test/store.test.ts
Original file line number Diff line number Diff line change
@@ -27,13 +27,14 @@ it('can update context with a property assigner', () => {
const store = createStore(
{ count: 0, greeting: 'hello' },
{
inc: {
count: (ctx) => ctx.count + 1
},
updateBoth: {
count: () => 42,
inc: (ctx) => ({
...ctx,
count: ctx.count + 1
}),
updateBoth: (ctx) => ({
count: 42,
greeting: 'hi'
}
})
}
);

@@ -52,9 +53,9 @@ it('handles unknown events (does not do anything)', () => {
const store = createStore(
{ count: 0 },
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
count: ctx.count + 1
})
}
);

@@ -165,7 +166,7 @@ it('createStoreWithProducer(…) infers the context type properly with a produce
count: 0
},
on: {
inc: (ctx, ev: { by: number }, enq) => {
inc: (ctx, ev: { by: number }) => {
ctx.count += ev.by;
}
}
@@ -180,9 +181,9 @@ it('can be observed', () => {
count: 0
},
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
count: ctx.count + 1
})
}
);

@@ -211,9 +212,9 @@ it('can be inspected', () => {
count: 0
},
{
inc: {
count: (ctx) => ctx.count + 1
}
inc: (ctx) => ({
count: ctx.count + 1
})
}
);

Loading