Skip to content

Commit

Permalink
feat(hook): add useRef hook and ref param
Browse files Browse the repository at this point in the history
  • Loading branch information
maxpowa committed Aug 26, 2024
1 parent 3e9988c commit 898a986
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 26 deletions.
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,27 @@ end)

### Features

- Function components
- Function components with hook support
- `useState`
- `useReducer`
- `useEffect`
- `useMemo`
- `useCallback`
- Plain text components (generated via `label`)
- Simple event API
- No need to register a separate event listener, just add a prop
- Supported events listed below
- `on_gui_checked_state_changed`
- `on_gui_click`
- `on_gui_confirmed`
- `on_gui_elem_changed`
- `on_gui_selection_state_changed`
- `on_gui_text_changed`
- `on_gui_value_changed`
-

### Coming Soon™

- Component shorthand similar to JSX
- Save/load event handler restoration

### Examples

// TODO






Expand Down
18 changes: 5 additions & 13 deletions lib/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,19 @@ local function is_array(value)
end

local function addElementToParent(v, parent, index)
-- internal helper to abstract swapping logic
local function addElement(props)
-- Since we can't directly insert elements at a specific index, we have to swap them around after adding
local element = parent.add(props)
if (index ~= #parent.children) then
parent.swap_children(index, #parent.children)
end
return element
end

if (v.type ~= nil) then
local props = {}

-- omit event handlers from props
for k, _ in pairs(v.props or {}) do
if (k:find("on_gui_") ~= 1) then
if (k:find("on_gui_") ~= 1 and k ~= "ref") then
props[k] = v.props[k]
end
end

return addElement(merge(props, { type = v.type }))
return parent.add(merge(props, { type = v.type, index = index }))
elseif (type(v) == "string") then
return addElement({ type = "label", caption = v })
return parent.add({ type = "label", caption = v, index = index })
else
error("Invalid element: " .. serpent.line(v))
end
Expand Down Expand Up @@ -115,6 +105,8 @@ local function render(vlist, parent, storage)
if (k:find("on_gui_") == 1 and type(v) == "function") then
-- add event handler cleanup functions to the storage table
createScopedHandler(defines.events[k], node, v, node.index)
elseif (k == "ref") then
v.current = node
elseif ((not createdNewNode) and compareNodeProp(node, k, v)) then
-- If we created a new node, we don't need to update it
node[k] = v
Expand Down
13 changes: 12 additions & 1 deletion lib/hooks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ local function useCallback(cb, deps)
return useMemo(function() return cb end, deps)
end

--- A hook that lets you store a mutable value
---
--- @param initialValue any initial value
--- @return table ref a mutable reference object
---
--- @see https://react.dev/reference/react/useRef
local function useRef(initialValue)
return useMemo(function() return { current = initialValue } end, {})
end

return {
-- Core util for hooks
enableHookContext = enableHookContext,
Expand All @@ -128,5 +138,6 @@ return {
useState = useState,
useEffect = useEffect,
useMemo = useMemo,
useCallback = useCallback
useCallback = useCallback,
useRef = useRef,
}
1 change: 1 addition & 0 deletions react.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ return {
useEffect = hooks.useEffect,
useMemo = hooks.useMemo,
useCallback = hooks.useCallback,
useRef = hooks.useRef,
}
4 changes: 2 additions & 2 deletions test/harness.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ local function TestHarness(props)
local tests = props.tests
local player = props.player

local current_test_index, setState = React.useState(2)
local current_test_index, setState = React.useState(1)
local autoRun, setAutoRun = React.useState(false)

local current_test = tests[current_test_index]
Expand All @@ -47,7 +47,7 @@ local function TestHarness(props)
setAutoRun(not autoRun)
end

useEffect(function()
React.useEffect(function()
local timeoutId = nil
if autoRun and current_test_index < #tests then
timeoutId = setTimeout(function()
Expand Down
1 change: 1 addition & 0 deletions test/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local tests = {
require("tests.01-basic"),
require("tests.02-typical"),
require("tests.03-basic-with-hooks"),
require("tests.04-useRef"),
require("tests.99-all-elements"),
}

Expand Down
19 changes: 19 additions & 0 deletions test/tests/04-useRef.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
local React = require("__react__.react")

local function Test(props)
local textbox = React.useRef(nil)

local on_press = React.useCallback(function()
if textbox.current then
textbox.current.focus()
textbox.current.select_all()
end
end, { textbox })

return React.createElement("flow", { direction = "vertical" },
React.createElement("text-box", { ref = textbox, text = "Hello, world!" }),
React.createElement("button", { on_gui_click = on_press, caption = "Select all"})
)
end

return Test

0 comments on commit 898a986

Please sign in to comment.