mirror of
https://github.com/littensy/charm.git
synced 2026-10-20 17:51:37 +00:00
feat!: replace split with mapAtom
This commit is contained in:
parent
fc3cd6d9ad
commit
4d4979daa9
11 changed files with 188 additions and 245 deletions
|
|
@ -57,7 +57,7 @@ Charm = "littensy/charm@VERSION"
|
|||
|
||||
#### `observe(atom, observer)`
|
||||
|
||||
#### `split(atom, keyExtractor?)`
|
||||
#### `mapAtom(atom, mapper)`
|
||||
|
||||
### Atom
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ Charm = "littensy/charm@VERSION"
|
|||
|
||||
#### `Atom.set(state)`
|
||||
|
||||
#### `Atom.memo(equalityFn)`
|
||||
#### `Atom.memo(areEqual)`
|
||||
|
||||
#### `Atom.readonly()`
|
||||
|
||||
|
|
|
|||
105
src/__tests__/mapAtom.spec.luau
Normal file
105
src/__tests__/mapAtom.spec.luau
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
local atom = require(script.Parent.Parent.atom)
|
||||
local mapAtom = require(script.Parent.Parent.mapAtom)
|
||||
local observe = require(script.Parent.Parent.observe)
|
||||
|
||||
return function()
|
||||
it("returns mapped atoms", function()
|
||||
local itemsAtom = atom({ "a", "b", "c" })
|
||||
local mappedAtom = mapAtom(itemsAtom, function(value, key)
|
||||
return string.upper(value), key
|
||||
end)
|
||||
local updates = 0
|
||||
|
||||
mappedAtom.listeners[function()
|
||||
updates += 1
|
||||
end] = true
|
||||
|
||||
for key = 1, 3 do
|
||||
local item = mappedAtom()[key]
|
||||
expect(item).to.equal(string.upper(itemsAtom()[key]))
|
||||
end
|
||||
|
||||
itemsAtom({ "d", "e", "f" })
|
||||
expect(updates).to.equal(1)
|
||||
|
||||
for key = 1, 3 do
|
||||
local item = mappedAtom()[key]
|
||||
expect(item).to.equal(string.upper(itemsAtom()[key]))
|
||||
end
|
||||
|
||||
itemsAtom({ [1] = "d", [3] = "f" })
|
||||
expect(mappedAtom()[2]).to.never.be.ok()
|
||||
expect(updates).to.equal(2)
|
||||
end)
|
||||
|
||||
it("uses key extractor", function()
|
||||
local itemsAtom = atom({ { key = 1 }, { key = 2 }, { key = 3 } })
|
||||
local mappedAtom = mapAtom(itemsAtom, function(item)
|
||||
return item, item.key
|
||||
end)
|
||||
|
||||
for key = 1, 3 do
|
||||
local item = mappedAtom()[key]
|
||||
expect(item).to.equal(itemsAtom()[key])
|
||||
end
|
||||
|
||||
local previous = mappedAtom()
|
||||
itemsAtom({ itemsAtom()[1], itemsAtom()[3] })
|
||||
|
||||
expect(mappedAtom()[1]).to.equal(previous[1])
|
||||
expect(mappedAtom()[1]).to.equal(itemsAtom()[1])
|
||||
expect(mappedAtom()[2]).to.never.be.ok()
|
||||
expect(mappedAtom()[3]).to.equal(previous[3])
|
||||
expect(mappedAtom()[3]).to.equal(itemsAtom()[2])
|
||||
end)
|
||||
|
||||
it("allows observe()", function()
|
||||
local itemsAtom = atom({
|
||||
{ key = 1, value = "a" },
|
||||
{ key = 2, value = "b" },
|
||||
{ key = 3, value = "c" },
|
||||
})
|
||||
|
||||
local mappedAtom = mapAtom(itemsAtom, function(item)
|
||||
return item, item.key
|
||||
end)
|
||||
|
||||
local added = 0
|
||||
local deleted = 0
|
||||
|
||||
local _, flush = observe(mappedAtom, function(item, key)
|
||||
expect(item).to.equal(itemsAtom()[key])
|
||||
added += 1
|
||||
|
||||
return function()
|
||||
expect(mappedAtom()[key]).to.never.be.ok()
|
||||
deleted += 1
|
||||
end
|
||||
end)
|
||||
|
||||
expect(added).to.equal(3)
|
||||
expect(deleted).to.equal(0)
|
||||
|
||||
itemsAtom({ itemsAtom()[1], itemsAtom()[3] })
|
||||
flush()
|
||||
|
||||
expect(added).to.equal(3)
|
||||
expect(deleted).to.equal(1)
|
||||
|
||||
itemsAtom({})
|
||||
flush()
|
||||
|
||||
expect(added).to.equal(3)
|
||||
expect(deleted).to.equal(3)
|
||||
end)
|
||||
|
||||
it("unmounts from parent atom", function()
|
||||
local itemsAtom = atom({})
|
||||
local mappedAtom = mapAtom(itemsAtom, function(item, key)
|
||||
return item, key
|
||||
end)
|
||||
expect((next(itemsAtom.listeners))).to.be.ok()
|
||||
mappedAtom:unmount()
|
||||
expect((next(itemsAtom.listeners))).to.never.be.ok()
|
||||
end)
|
||||
end
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
local atom = require(script.Parent.Parent.atom)
|
||||
local isAtom = require(script.Parent.Parent.isAtom)
|
||||
local observe = require(script.Parent.Parent.observe)
|
||||
local split = require(script.Parent.Parent.split)
|
||||
|
||||
return function()
|
||||
it("returns split atoms", function()
|
||||
local itemsAtom = atom({ "a", "b", "c" })
|
||||
local itemAtoms = split(itemsAtom)
|
||||
local updates = 0
|
||||
|
||||
itemAtoms.listeners[function()
|
||||
updates += 1
|
||||
end] = true
|
||||
|
||||
for key = 1, 3 do
|
||||
local itemAtom = itemAtoms()[key]
|
||||
assert(isAtom(itemAtom), `itemAtom {key} is not an atom; got {typeof(itemAtom)}`)
|
||||
expect(itemAtom()).to.equal(itemsAtom()[key])
|
||||
end
|
||||
|
||||
local prevAtoms = itemAtoms()
|
||||
itemsAtom({ "d", "e", "f" })
|
||||
expect(updates).to.equal(1)
|
||||
|
||||
for key = 1, 3 do
|
||||
local itemAtom = itemAtoms()[key]
|
||||
assert(itemAtom == prevAtoms[key] :: any, `itemAtom {key} is not the same atom; got {typeof(itemAtom)}`)
|
||||
expect(itemAtom()).to.equal(itemsAtom()[key])
|
||||
end
|
||||
|
||||
itemsAtom({ [1] = "d", [3] = "f" })
|
||||
expect(itemAtoms()[2]).to.never.be.ok()
|
||||
expect(updates).to.equal(2)
|
||||
end)
|
||||
|
||||
it("has writable items", function()
|
||||
local itemsAtom = atom({ "a", "b", "c" })
|
||||
local itemAtoms = split(itemsAtom)
|
||||
local updates = 0
|
||||
|
||||
itemAtoms.listeners[function()
|
||||
updates += 1
|
||||
end] = true
|
||||
|
||||
itemAtoms()[2]("x")
|
||||
expect(updates).to.equal(1)
|
||||
expect(itemsAtom()[2]).to.equal("x")
|
||||
expect(itemAtoms()[2]()).to.equal("x")
|
||||
end)
|
||||
|
||||
it("uses key extractor", function()
|
||||
local itemsAtom = atom({ { key = 1 }, { key = 2 }, { key = 3 } })
|
||||
local itemAtoms = split(itemsAtom, function(item)
|
||||
return item.key
|
||||
end)
|
||||
|
||||
for key = 1, 3 do
|
||||
local itemAtom = itemAtoms()[key]
|
||||
expect(itemAtom()).to.equal(itemsAtom()[key])
|
||||
end
|
||||
|
||||
local prevAtoms = itemAtoms()
|
||||
itemsAtom({ itemsAtom()[1], itemsAtom()[3] })
|
||||
|
||||
expect(itemAtoms()[1]).to.equal(prevAtoms[1])
|
||||
expect(itemAtoms()[1]()).to.equal(itemsAtom()[1])
|
||||
expect(itemAtoms()[2]).to.never.be.ok()
|
||||
expect(itemAtoms()[3]).to.equal(prevAtoms[3])
|
||||
expect(itemAtoms()[3]()).to.equal(itemsAtom()[2])
|
||||
end)
|
||||
|
||||
it("allows observe()", function()
|
||||
local itemsAtom = atom({
|
||||
{ key = 1, value = "a" },
|
||||
{ key = 2, value = "b" },
|
||||
{ key = 3, value = "c" },
|
||||
})
|
||||
local itemAtoms = split(itemsAtom, function(item)
|
||||
return item.key
|
||||
end)
|
||||
|
||||
local added = 0
|
||||
local updated = 0
|
||||
local deleted = 0
|
||||
|
||||
local _, flush = observe(itemAtoms, function(item, key)
|
||||
expect(item()).to.equal(itemsAtom()[key])
|
||||
|
||||
item.listeners[function(value)
|
||||
expect(value).to.equal(itemsAtom()[key])
|
||||
updated += 1
|
||||
end] = true
|
||||
|
||||
added += 1
|
||||
|
||||
return function()
|
||||
expect(itemAtoms()[key]).to.never.be.ok()
|
||||
deleted += 1
|
||||
end
|
||||
end)
|
||||
|
||||
expect(added).to.equal(3)
|
||||
expect(updated).to.equal(0)
|
||||
expect(deleted).to.equal(0)
|
||||
|
||||
itemsAtom({ itemsAtom()[1], itemsAtom()[3] })
|
||||
flush()
|
||||
|
||||
expect(added).to.equal(3)
|
||||
expect(updated).to.equal(0)
|
||||
expect(deleted).to.equal(1)
|
||||
|
||||
itemAtoms()[1]({ key = 1, value = "x" })
|
||||
flush()
|
||||
|
||||
expect(added).to.equal(3)
|
||||
expect(updated).to.equal(1)
|
||||
expect(deleted).to.equal(1)
|
||||
|
||||
itemsAtom({})
|
||||
flush()
|
||||
|
||||
expect(added).to.equal(3)
|
||||
expect(updated).to.equal(1)
|
||||
expect(deleted).to.equal(3)
|
||||
end)
|
||||
|
||||
it("unmounts from parent atom", function()
|
||||
local itemsAtom = atom({})
|
||||
local itemAtoms = split(itemsAtom)
|
||||
expect((next(itemsAtom.listeners))).to.be.ok()
|
||||
itemAtoms:unmount()
|
||||
expect((next(itemsAtom.listeners))).to.never.be.ok()
|
||||
end)
|
||||
end
|
||||
|
|
@ -11,7 +11,7 @@ type AtomInternal<T> = {
|
|||
export type Atom<T> = (() -> T) & ((state: T | ((T) -> T)) -> ()) & {
|
||||
get: ((self: Atom<T>) -> T) & (<Result>(self: Atom<T>, selector: (T) -> Result) -> Result),
|
||||
set: (self: Atom<T>, state: T | ((T) -> T)) -> (),
|
||||
memo: (self: Atom<T>, equalityFn: (previous: T, current: T) -> boolean) -> Atom<T>,
|
||||
memo: (self: Atom<T>, areEqual: (previous: T, current: T) -> boolean) -> Atom<T>,
|
||||
writable: (self: Atom<T>, onSet: ((update: T, previous: T) -> T?) & (update: T, previous: T) -> ()) -> Atom<T>,
|
||||
readonly: (self: Atom<T>) -> ReadonlyAtom<T>,
|
||||
named: (self: Atom<T>, label: string) -> Atom<T>,
|
||||
|
|
@ -20,7 +20,7 @@ export type Atom<T> = (() -> T) & ((state: T | ((T) -> T)) -> ()) & {
|
|||
|
||||
export type ReadonlyAtom<T> = (() -> T) & {
|
||||
get: ((self: ReadonlyAtom<T>) -> T) & (<Result>(self: ReadonlyAtom<T>, selector: (T) -> Result) -> Result),
|
||||
memo: (self: ReadonlyAtom<T>, equalityFn: (previous: T, current: T) -> boolean) -> ReadonlyAtom<T>,
|
||||
memo: (self: ReadonlyAtom<T>, areEqual: (previous: T, current: T) -> boolean) -> ReadonlyAtom<T>,
|
||||
writable: ((self: Atom<T>) -> Atom<T>)
|
||||
& ((self: Atom<T>, onSet: ((update: T, previous: T) -> T?) & (update: T, previous: T) -> ()) -> Atom<T>),
|
||||
readonly: (self: ReadonlyAtom<T>) -> ReadonlyAtom<T>,
|
||||
|
|
@ -81,8 +81,8 @@ local function atom<State>(state: State): Atom<State>
|
|||
end
|
||||
end
|
||||
|
||||
function atom:memo(equalityFn: (State, State) -> boolean): Atom<State>
|
||||
equals = equalityFn
|
||||
function atom:memo(areEqual: (State, State) -> boolean): Atom<State>
|
||||
equals = areEqual
|
||||
return self
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
local atom = require(script.Parent.atom)
|
||||
type Atom<T> = atom.Atom<T>
|
||||
type ReadonlyAtom<T> = atom.ReadonlyAtom<T>
|
||||
|
||||
local reference = require(script.Parent.utils.reference)
|
||||
|
||||
type Derive =
|
||||
|
|
@ -29,7 +28,7 @@ local function derive<Result>(...): ReadonlyAtom<Result>
|
|||
local getDerivedAtom = reference.weak(derivedAtom)
|
||||
|
||||
function derivedAtom:unmount()
|
||||
derivedAtomUnmount(derivedAtom)
|
||||
derivedAtomUnmount(self)
|
||||
|
||||
for listener in listeners do
|
||||
for _, atom in atoms do
|
||||
|
|
|
|||
31
src/index.d.ts
vendored
31
src/index.d.ts
vendored
|
|
@ -17,7 +17,7 @@ declare namespace Charm {
|
|||
get(): State;
|
||||
get<Result>(selector: (state: State) => Result): Result;
|
||||
set(state: State | ((current: State) => State)): void;
|
||||
memo(equalityFn: (previous: State, current: State) => boolean): this;
|
||||
memo(areEqual: (previous: State, current: State) => boolean): this;
|
||||
writable(onSet?: (update: State, previous: State) => State | void): Atom<State>;
|
||||
readonly(): ReadonlyAtom<State>;
|
||||
named(label: string): this;
|
||||
|
|
@ -55,22 +55,25 @@ declare namespace Charm {
|
|||
|
||||
function observe(atom: Atom<any>, factory: (value: unknown, key: unknown) => (() => void) | void): () => void;
|
||||
|
||||
function splitAtom<Item, Key, Id = Key>(
|
||||
listAtom: Atom<ReadonlyMap<Key, Item>> | Atom<Map<Key, Item>>,
|
||||
keyExtractor?: (item: Item) => Id,
|
||||
): Atom<ReadonlyMap<Id, Atom<Item>>>;
|
||||
function mapAtom<V0, K1, V1>(
|
||||
atom: Atom<readonly V0[]> | Atom<V0[]>,
|
||||
mapper: (value: V0, index: number) => LuaTuple<[V1 | undefined, K1]>,
|
||||
): ReadonlyAtom<ReadonlyMap<K1, V1>>;
|
||||
|
||||
function splitAtom<Item>(listAtom: Atom<readonly Item[]>): Atom<readonly Atom<Item>[]>;
|
||||
function mapAtom<V0, V1>(
|
||||
atom: Atom<readonly V0[]> | Atom<V0[]>,
|
||||
mapper: (value: V0, index: number) => V1,
|
||||
): ReadonlyAtom<readonly V1[]>;
|
||||
|
||||
function splitAtom<Item, Id = number>(
|
||||
listAtom: Atom<readonly Item[]>,
|
||||
keyExtractor?: (item: Item) => Id,
|
||||
): Atom<ReadonlyMap<Id, Atom<Item>>>;
|
||||
function mapAtom<K0, V0, K1 = K0, V1 = V0>(
|
||||
atom: Atom<ReadonlyMap<K0, V0>> | Atom<Map<K0, V0>>,
|
||||
mapper: (value: V0, key: K0) => LuaTuple<[V1 | undefined, K1]> | V1,
|
||||
): ReadonlyAtom<ReadonlyMap<K1, V1>>;
|
||||
|
||||
function splitAtom<Item, Key extends string | number | symbol, Id = Key>(
|
||||
listAtom: Atom<{ readonly [K in Key]?: Item }>,
|
||||
keyExtractor?: (item: Item) => Id,
|
||||
): Atom<ReadonlyMap<Id, Atom<Item>>>;
|
||||
function mapAtom<K0 extends string | number | symbol, V0, K1, V1>(
|
||||
atom: Atom<{ readonly [K in K0]: V0 }>,
|
||||
mapper: (value: V0, key: K0) => LuaTuple<[V1 | undefined, K1]> | V1,
|
||||
): ReadonlyAtom<ReadonlyMap<K1, V1>>;
|
||||
|
||||
function useAtomState<State>(atom: Atom<State>): State;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ _G[script.modules.Promise] = _G[script]
|
|||
local atom = require(script.atom)
|
||||
local derive = require(script.derive)
|
||||
local isAtom = require(script.isAtom)
|
||||
local mapAtom = require(script.mapAtom)
|
||||
local observe = require(script.observe)
|
||||
local split = require(script.split)
|
||||
local subscribe = require(script.subscribe)
|
||||
local useAtom = require(script.react.useAtom)
|
||||
local useAtomState = require(script.react.useAtomState)
|
||||
local useSetAtom = require(script.react.useSetAtom)
|
||||
local subscribe = require(script.subscribe)
|
||||
local sync = require(script.sync)
|
||||
|
||||
export type Atom<State> = atom.Atom<State>
|
||||
|
|
@ -19,7 +19,7 @@ return {
|
|||
derive = derive,
|
||||
isAtom = isAtom,
|
||||
observe = observe,
|
||||
split = split,
|
||||
mapAtom = mapAtom,
|
||||
subscribe = subscribe,
|
||||
sync = sync,
|
||||
useAtom = useAtom,
|
||||
|
|
|
|||
56
src/mapAtom.luau
Normal file
56
src/mapAtom.luau
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
local atom = require(script.Parent.atom)
|
||||
type Atom<T> = atom.Atom<T>
|
||||
type ReadonlyAtom<T> = atom.ReadonlyAtom<T>
|
||||
local reference = require(script.Parent.utils.reference)
|
||||
|
||||
local function mapAtom<K0, V0, K1, V1>(itemsAtom: Atom<{ [K0]: V0 }>, mapper: (V0, K0) -> (V1?, K1?)): ReadonlyAtom<{ [K1]: V1 }>
|
||||
local mappedAtom: ReadonlyAtom<{ [K1]: V1 }> = atom({}):readonly()
|
||||
local mappedAtomUnmount = mappedAtom.unmount
|
||||
local getMappedAtom = reference.weak(mappedAtom)
|
||||
local previousMappedItems: { [K1]: V1 } = {}
|
||||
|
||||
local function listener(items)
|
||||
local mappedAtom = getMappedAtom()
|
||||
|
||||
if not mappedAtom then
|
||||
itemsAtom.listeners[listener] = nil
|
||||
return
|
||||
end
|
||||
|
||||
local mappedItems = table.clone(mappedAtom:get())
|
||||
local mappedKeys = {}
|
||||
|
||||
for key, item in items do
|
||||
local newItem, newKey = mapper(item, key)
|
||||
if newKey == nil then
|
||||
newKey = key :: any
|
||||
end
|
||||
if mappedItems[newKey :: K1] ~= newItem then
|
||||
mappedItems[newKey :: K1] = newItem :: V1
|
||||
else
|
||||
mappedKeys[newKey] = key
|
||||
end
|
||||
end
|
||||
|
||||
for key in previousMappedItems do
|
||||
if mappedKeys[key] == nil and mappedItems[key] == previousMappedItems[key] then
|
||||
mappedItems[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
previousMappedItems = mappedItems
|
||||
mappedAtom:setForced(mappedItems)
|
||||
end
|
||||
|
||||
function mappedAtom:unmount()
|
||||
mappedAtomUnmount(self)
|
||||
itemsAtom.listeners[listener] = nil
|
||||
end
|
||||
|
||||
itemsAtom.listeners[listener] = true
|
||||
listener(itemsAtom:get())
|
||||
|
||||
return mappedAtom
|
||||
end
|
||||
|
||||
return mapAtom
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
local atom = require(script.Parent.atom)
|
||||
type Atom<T> = atom.Atom<T>
|
||||
|
||||
local subscribe = require(script.Parent.subscribe)
|
||||
|
||||
local function observe<Item, Key>(atom: Atom<{ [Key]: Item }>, factory: (value: Item, key: Key) -> () -> ()): (() -> (), () -> ())
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
local atom = require(script.Parent.atom)
|
||||
type Atom<T> = atom.Atom<T>
|
||||
type ReadonlyAtom<T> = atom.ReadonlyAtom<T>
|
||||
|
||||
local reference = require(script.Parent.utils.reference)
|
||||
|
||||
type Split =
|
||||
(<Item, Key>(atom: Atom<{ [Key]: Item }>) -> ReadonlyAtom<{ [Key]: Atom<Item> }>)
|
||||
& (<Item, Key>(atom: Atom<{ [any]: Item }>, keyExtractor: (Item) -> Key) -> ReadonlyAtom<{ [Key]: Atom<Item> }>)
|
||||
|
||||
local function split<Item, Key>(
|
||||
itemsAtom: Atom<{ [Key]: Item }>,
|
||||
keyExtractor: ((Item) -> Key)?
|
||||
): ReadonlyAtom<{ [Key]: Atom<Item> }>
|
||||
local splitAtom: ReadonlyAtom<{ [Key]: Atom<Item> }> = atom({}):readonly()
|
||||
local splitAtomUnmount = splitAtom.unmount
|
||||
local getSplitAtom = reference.weak(splitAtom)
|
||||
|
||||
local previousAtoms: { [Key]: Atom<Item> } = {}
|
||||
local previousItems = itemsAtom:get()
|
||||
local listener: ({ [Key]: Item }) -> ()
|
||||
|
||||
local function cleanup()
|
||||
itemsAtom.listeners[listener] = nil
|
||||
for _, atom in previousAtoms do
|
||||
atom:unmount()
|
||||
end
|
||||
end
|
||||
|
||||
function listener(items)
|
||||
local splitAtom = getSplitAtom()
|
||||
|
||||
if not splitAtom then
|
||||
return cleanup()
|
||||
end
|
||||
|
||||
local atoms = table.clone(splitAtom:get())
|
||||
local itemsByKeys: { [Key]: Item } = {}
|
||||
|
||||
for key, item in items do
|
||||
local id = if keyExtractor then keyExtractor(item) else key
|
||||
|
||||
itemsByKeys[id] = item
|
||||
|
||||
if atoms[id] then
|
||||
-- Force set the atom to avoid redundantly updating itemsAtom
|
||||
atoms[id]:setForced(item)
|
||||
else
|
||||
atoms[id] = atom(item):writable(function(update)
|
||||
local nextItems = table.clone(itemsAtom:get())
|
||||
nextItems[key] = update
|
||||
itemsAtom:set(nextItems)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
for key, item in previousItems do
|
||||
local id = if keyExtractor then keyExtractor(item) else key
|
||||
local atom = atoms[id]
|
||||
|
||||
if itemsByKeys[id] == nil and atom then
|
||||
atom:unmount()
|
||||
atoms[id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
previousAtoms, previousItems = atoms, items
|
||||
splitAtom:setForced(atoms)
|
||||
end
|
||||
|
||||
function splitAtom:unmount()
|
||||
splitAtomUnmount(splitAtom)
|
||||
cleanup()
|
||||
end
|
||||
|
||||
itemsAtom.listeners[listener] = true
|
||||
listener(itemsAtom:get())
|
||||
|
||||
return splitAtom
|
||||
end
|
||||
|
||||
return (split :: any) :: Split
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
local atom = require(script.Parent.atom)
|
||||
type Atom<T> = atom.Atom<T>
|
||||
|
||||
local reference = require(script.Parent.utils.reference)
|
||||
|
||||
local function subscribe<State>(atom: Atom<State>, callback: (state: State, previous: State) -> ()): (() -> (), () -> ())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue