Working better now (but see notes)
Some checks failed
NeoVim tests / code-quality (push) Failing after 1m18s

Even so, some things can get screwy if the component tree sets
suspicious `key`s. Like:

```lua
{
  'String 1',                    -- auto-assigned key = idx(1)
  h(Component, { key = 1 }, {}), -- conflicting key(1)
  'String 2',                    -- auto-assigned key = idx(3)
  h(Component, { key = 2 }, {}), -- key = 2
}
```

In this example, there are two elements competing for key `1`, which
causes weird render issues.
This commit is contained in:
Jonathan Apodaca 2025-10-09 22:49:01 -06:00
parent 8fa2784bf7
commit fdb2a49638

View File

@ -86,7 +86,7 @@ M.Renderer = Renderer
--- component?: (fun(component: u.renderer.FnComponent, tag: u.renderer.Tag): any),
--- unknown?: fun(tag: any): any
--- }
function Renderer.tree_match(tree, visitors)-- {{{
function Renderer.tree_match(tree, visitors) -- {{{
local function is_tag(x) return type(x) == 'table' and x.kind == 'tag' end
local function is_tag_arr(x) return type(x) == 'table' and not is_tag(x) end
@ -108,7 +108,7 @@ function Renderer.tree_match(tree, visitors)-- {{{
else
return visitors.unknown and visitors.unknown(tree) or error 'unknown value: not a tag'
end
end-- }}}
end -- }}}
--- @private
--- @param tree u.renderer.Tree
@ -788,15 +788,15 @@ function Renderer:mount(tree) -- {{{
or nil
if old_tree_kind == new_tree_kind then self.component_tree.ctx_by_node[old_tree] = nil end
--- @param new_tree u.renderer.Tree
--- @param ctx u.renderer.FnComponentContext
--- @param rendered_component u.renderer.Tree
local function update_ctx_and_recurse(new_tree, ctx, rendered_component)
local function update_ctx_and_recurse(ctx, rendered_component)
self.component_tree.ctx_by_node[new_tree] = ctx
-- The rendered component could have other components in its tree:
-- recurse and simplify:
local new_tree_simplified = H2.visit_tree(ctx.prev_tree, rendered_component)
ctx.prev_tree = new_tree_simplified
local new_tree_simplified =
M.h('text', ctx.props, H2.visit_tree(ctx.prev_tree, rendered_component))
ctx.prev_tree = rendered_component
return new_tree_simplified
end
@ -804,13 +804,13 @@ function Renderer:mount(tree) -- {{{
old_ctx.phase = 'update'
old_ctx.props = new_tag.attributes
old_ctx.children = new_tag.children
return update_ctx_and_recurse(new_tree, old_ctx, NewC(old_ctx))
return update_ctx_and_recurse(old_ctx, NewC(old_ctx))
else
local state = tracker.create_signal(nil)
--- @type u.renderer.FnComponentContext
local ctx = {
phase = old_tree and 'update' or 'mount',
phase = 'mount',
props = new_tag.attributes,
children = new_tag.children,
state = state --[[@as any]],
@ -822,7 +822,7 @@ function Renderer:mount(tree) -- {{{
-- first-renders to initialize the state without firing a
-- re-render:
ctx.unsubscribe = state:subscribe(render)
return update_ctx_and_recurse(new_tree, ctx, rendered)
return update_ctx_and_recurse(ctx, rendered)
end
end,
})
@ -836,29 +836,33 @@ function Renderer:mount(tree) -- {{{
--- @param new_arr u.renderer.Node[]
--- @return u.renderer.Node[]
function H2.visit_array(old_arr, new_arr)
--- @type u.renderer.Node[]
local old_to_unmount = {}
--- @type table<any, u.renderer.Node>
local old_by_key = {}
for _, old in ipairs(old_arr or {}) do
local key = Renderer.tree_match(old, {
tag = function(t) return t.attributes.key end,
component = function(_, t) return t.attributes.key end,
--- @param tree u.renderer.Tree
--- @param idx integer
local function get_or_set_key(tree, idx)
return Renderer.tree_match(tree, {
tag = function(t)
if not t.attributes.key then t.attributes.key = idx end
return t.attributes.key
end,
component = function(_, t)
if not t.attributes.key then t.attributes.key = idx end
return t.attributes.key
end,
})
if not key then
table.insert(old_to_unmount, old)
else
old_by_key[key] = old
end
end
for idx, old in ipairs(old_arr or {}) do
local key = get_or_set_key(old, idx)
if key then old_by_key[key] = old end
end
--- @type u.renderer.Node[]
local resulting_nodes = {}
for _, new in ipairs(new_arr) do
local key = Renderer.tree_match(new, {
tag = function(t) return t.attributes.key end,
component = function(_, t) return t.attributes.key end,
})
for idx, new in ipairs(new_arr) do
local key = get_or_set_key(new, idx)
local old = nil
if key then
old = old_by_key[key]
@ -867,10 +871,8 @@ function Renderer:mount(tree) -- {{{
table.insert(resulting_nodes, H2.visit_tree(old, new))
end
-- Unmount whatever is left in the "old" map:
for _, n in pairs(old_by_key) do
table.insert(old_to_unmount, n)
end
for _, n in pairs(old_to_unmount) do
H2.unmount(n)
end