From f2f7a1659974091da5285135d4019c252dffb287 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 3 May 2024 01:02:42 -0700 Subject: [PATCH] refactor!: minimal react api --- README.md | 6 +--- src/__tests__/react/useAtom.spec.luau | 42 ++++++++++++++++++++++ src/__tests__/react/useAtomState.spec.luau | 27 -------------- src/__tests__/react/useSetAtom.spec.luau | 34 ------------------ src/index.d.ts | 15 +------- src/init.luau | 4 --- src/modules/ReactRoblox.luau | 2 +- src/react/useAtom.luau | 27 +++++++------- src/react/useAtomState.luau | 31 ---------------- src/react/useSetAtom.luau | 15 -------- 10 files changed, 59 insertions(+), 144 deletions(-) create mode 100644 src/__tests__/react/useAtom.spec.luau delete mode 100644 src/__tests__/react/useAtomState.spec.luau delete mode 100644 src/__tests__/react/useSetAtom.spec.luau delete mode 100644 src/react/useAtomState.luau delete mode 100644 src/react/useSetAtom.luau diff --git a/README.md b/README.md index 9ba3790..152dd70 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,7 @@ Charm = "littensy/charm@VERSION" ### React -#### `useAtom(atom, selector?)` - -#### `useAtomState(atom)` - -#### `useSetAtom(atom)` +#### `useAtom(atom, dependencies?)` ### Sync diff --git a/src/__tests__/react/useAtom.spec.luau b/src/__tests__/react/useAtom.spec.luau new file mode 100644 index 0000000..e1cf6a2 --- /dev/null +++ b/src/__tests__/react/useAtom.spec.luau @@ -0,0 +1,42 @@ +local atom = require(script.Parent.Parent.Parent.atom) +local useAtom = require(script.Parent.Parent.Parent.react.useAtom) +local renderHook = require(script.Parent.helpers.renderHook) + +return function() + it("tracks atom state", function() + local source = atom(0) + local hook = renderHook(function() + return useAtom(source) + end) + expect(hook.result).to.equal(0) + source(1) + expect(hook.result).to.equal(1) + end) + + it("accepts a molecule", function() + local source = atom(0) + local hook = renderHook(function() + return useAtom(function() + return 2 * source() + end) + end) + expect(hook.result).to.equal(0) + source(1) + expect(hook.result).to.equal(2) + end) + + it("tracks dependencies", function() + local source = atom(0) + local hook = renderHook(function() + return useAtom(source, { source }) + end) + expect(hook.result).to.equal(0) + source(1) + expect(hook.result).to.equal(1) + source = atom(0) + hook.rerender() + expect(hook.result).to.equal(0) + source(1) + expect(hook.result).to.equal(1) + end) +end diff --git a/src/__tests__/react/useAtomState.spec.luau b/src/__tests__/react/useAtomState.spec.luau deleted file mode 100644 index 9c3824b..0000000 --- a/src/__tests__/react/useAtomState.spec.luau +++ /dev/null @@ -1,27 +0,0 @@ -local atom = require(script.Parent.Parent.Parent.atom) -local useAtomState = require(script.Parent.Parent.Parent.react.useAtomState) -local renderHook = require(script.Parent.helpers.renderHook) - -return function() - it("tracks atom state", function() - local source = atom(0) - local hook = renderHook(function() - return useAtomState(source) - end) - expect(hook.result).to.equal(0) - source(1) - expect(hook.result).to.equal(1) - end) - - it("receives a selector", function() - local source = atom(0) - local hook = renderHook(function() - return useAtomState(source, function(value) - return 2 * value - end) - end) - expect(hook.result).to.equal(0) - source(1) - expect(hook.result).to.equal(2) - end) -end diff --git a/src/__tests__/react/useSetAtom.spec.luau b/src/__tests__/react/useSetAtom.spec.luau deleted file mode 100644 index 325af5f..0000000 --- a/src/__tests__/react/useSetAtom.spec.luau +++ /dev/null @@ -1,34 +0,0 @@ -local atom = require(script.Parent.Parent.Parent.atom) -local useSetAtom = require(script.Parent.Parent.Parent.react.useSetAtom) -local renderHook = require(script.Parent.helpers.renderHook) - -return function() - it("sets atom state", function() - local source = atom(0) - local hook = renderHook(function() - return useSetAtom(source) - end) - expect(hook.result).to.be.a("function") - hook.result(1) - expect(source()).to.equal(1) - end) - - it("returns a stable function", function() - local source = atom(0) - local hook = renderHook(function() - return useSetAtom(source) - end) - local fn = hook.result - hook.rerender() - expect(hook.result).to.equal(fn) - end) - - it("memoizes the function", function() - local hook = renderHook(function(props) - return useSetAtom(props.source) - end, { source = atom(0) }) - local fn = hook.result - hook.rerender({ source = atom(1) }) - expect(hook.result).to.never.equal(fn) - end) -end diff --git a/src/index.d.ts b/src/index.d.ts index a0055a1..e878179 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -65,20 +65,7 @@ declare namespace Charm { // React - function useAtomState(molecule: Molecule): State; - - function useAtomState(molecule: Molecule, selector: (state: State) => Result): Result; - - function useSetAtom(molecule: Molecule): (state: State | ((prev: State) => State)) => void; - - function useAtom( - molecule: Molecule, - ): LuaTuple<[state: State, setState: (state: State | ((prev: State) => State)) => void]>; - - function useAtom( - molecule: Molecule, - selector: (state: State) => Result, - ): LuaTuple<[state: Result, setState: (state: Result | ((prev: Result) => Result)) => void]>; + function useAtom(molecule: Molecule, dependencies?: unknown[]): State; // Sync diff --git a/src/init.luau b/src/init.luau index 1c42892..867529e 100644 --- a/src/init.luau +++ b/src/init.luau @@ -7,8 +7,6 @@ local map = require(script.map) local observe = require(script.observe) local peek = require(script.peek) local useAtom = require(script.react.useAtom) -local useAtomState = require(script.react.useAtomState) -local useSetAtom = require(script.react.useSetAtom) local store = require(script.store) local subscribe = require(script.subscribe) local sync = require(script.sync) @@ -26,8 +24,6 @@ return { observe = observe, peek = peek, useAtom = useAtom, - useAtomState = useAtomState, - useSetAtom = useSetAtom, batch = store.batch, capture = store.capture, notify = store.notify, diff --git a/src/modules/ReactRoblox.luau b/src/modules/ReactRoblox.luau index 9c41f68..d5df9b2 100644 --- a/src/modules/ReactRoblox.luau +++ b/src/modules/ReactRoblox.luau @@ -5,5 +5,5 @@ local Packages = if Package.Parent:FindFirstChild("ReactRoblox") type ReactRoblox = typeof(require(Package.Parent.ReactRoblox)) --- [INFO] It's safe to require this module since it's only used in unit tests. +-- It's safe to require this module since it's only used in unit tests. return (if Packages then require(Packages.ReactRoblox) else nil) :: ReactRoblox diff --git a/src/react/useAtom.luau b/src/react/useAtom.luau index 3dad656..f35d7e7 100644 --- a/src/react/useAtom.luau +++ b/src/react/useAtom.luau @@ -1,18 +1,19 @@ +local requireReact = require(script.Parent.Parent.modules.React) +local subscribe = require(script.Parent.Parent.subscribe) local types = require(script.Parent.Parent.types) -type Atom = types.Atom -local useAtomState = require(script.Parent.useAtomState) -local useSetAtom = require(script.Parent.useSetAtom) +type Molecule = types.Molecule -type UseAtom = - ((atom: Atom) -> (State, useSetAtom.SetState)) - & (( - atom: Atom, - selector: ((State) -> Result)? - ) -> (State | Result, useSetAtom.SetState)) - & ((atom: Atom, selector: (State) -> Result) -> (Result, useSetAtom.SetState)) +local function useAtom(molecule: Molecule, dependencies: { any }?): State + local React = requireReact() -local function useAtom(atom: Atom, selector: ((State) -> State)?): (State, useSetAtom.SetState) - return useAtomState(atom, selector), useSetAtom(atom) + local state, setState = React.useState(molecule) + + React.useEffect(function() + setState(molecule()) + return subscribe(molecule, setState) + end, dependencies or {}) + + return state end -return useAtom :: UseAtom +return useAtom diff --git a/src/react/useAtomState.luau b/src/react/useAtomState.luau deleted file mode 100644 index dbac9c3..0000000 --- a/src/react/useAtomState.luau +++ /dev/null @@ -1,31 +0,0 @@ -local requireReact = require(script.Parent.Parent.modules.React) -local subscribe = require(script.Parent.Parent.subscribe) -local types = require(script.Parent.Parent.types) -type Atom = types.Atom - -type UseAtomState = - ((atom: Atom) -> State) - & ((atom: Atom, ((State) -> Result)?) -> State | Result) - & ((atom: Atom, selector: (State) -> Result) -> Result) - -local function useAtomState(atom: Atom, selector: ((State) -> State)?): State - local React = requireReact() - - local state, setState = React.useState(function() - return if selector then selector(atom()) else atom() - end) - - local latestSelector = React.useRef(selector) - latestSelector.current = selector - - React.useEffect(function() - return subscribe(atom, function(state) - local selector = latestSelector.current - setState(if selector then selector(state) else state) - end) - end, { atom }) - - return state -end - -return useAtomState :: UseAtomState diff --git a/src/react/useSetAtom.luau b/src/react/useSetAtom.luau deleted file mode 100644 index 3c6c370..0000000 --- a/src/react/useSetAtom.luau +++ /dev/null @@ -1,15 +0,0 @@ -local requireReact = require(script.Parent.Parent.modules.React) -local types = require(script.Parent.Parent.types) -type Atom = types.Atom - -export type SetState = (T | ((T) -> T)) -> () - -local function useSetAtom(atom: Atom): SetState - local React = requireReact() - - return React.useCallback(function(state: State | ((State) -> State)): () - atom(state) - end, { atom }) -end - -return useSetAtom