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

Reactivity in lists only in the updated element #8

Open
aralroca opened this issue Nov 23, 2023 · 8 comments
Open

Reactivity in lists only in the updated element #8

aralroca opened this issue Nov 23, 2023 · 8 comments
Assignees
Labels
bug Something isn't working high

Comments

@aralroca
Copy link
Collaborator

aralroca commented Nov 23, 2023

This issue happens in a web component with a list with a signal, whenever the list is updated, instead of affecting only the modified field, the whole list is updated. It goes well anyway, but it would be an optimization to do.

Example of Component to reproduce it:

import type { WebContext } from 'brisa'

export default function MagicList({ }, { state }: WebContext) {
  const list = state<string[]>(['some', 'another']);

  const addItem = (e: any) => {
    e.preventDefault();
    const formData = new FormData(e.target)
    list.value = [...list.value, formData.get('item') as string]
  }

  const deleteItem = (index: number) => {
    list.value = list.value.filter((_, i) => i !== index)
  }

  const moveItemUp = (index: number) => {
    if (index === 0) return
    const item = list.value[index]
    const filtered = list.value.filter((_, i) => i !== index)
    list.value = [...filtered.slice(0, index - 1), item, ..filtered.slice(index - 1)]
  }

  return (
    <div>
      <form onSubmit={addItem}>
        <input name="item" id="item" placeholder="Add item" />
        <button>add</button>
      </form>
      <ul>
        {list.value.map((item: string, index: number) => (
          <li>
            <button onClick={() => deleteItem(index)}>delete</button>
            <button onClick={() => moveItemUp(index)}>move up</button>
            {item}
          </li>
        ))}
      </ul>
    </div>
  )
}

error

@aralroca aralroca added the bug Something isn't working label Nov 23, 2023
@aralroca
Copy link
Collaborator Author

aralroca commented Dec 5, 2023

Also if the items of the list are web components they are losing the state (disconnecting and connecting) when the list is updated

@aralroca aralroca self-assigned this Feb 9, 2024
@aralroca
Copy link
Collaborator Author

aralroca commented Apr 16, 2024

According to Ryan Carniato there is no choice but to use reconciliation to solve this.

So this means reuse https://github.com/aralroca/diff-dom-streaming ?

It would be great to find an alternative to add this code, right now it loads in a lazy way for server actions... Adding it when loading the page would go from occupying 3kb to occupy 5kb... The benefit that then it would not be necessary to load it in a lazy way, but it is already 2kb approx to solve the lists... Let's see if we find a better way.

@aralroca
Copy link
Collaborator Author

aralroca commented May 15, 2024

Good news, this should solve the problem with lists and signals: https://twitter.com/aralroca/status/1790730464640033008 The bad part is that you have to wait for browsers to support it, but it looks good.

Code example: https://github.com/bigskysoftware/chrome-movebefore-demo
GH thread: whatwg/dom#1255
Some PR example lit-html: https://github.com/lit/lit/pull/4838/files#diff-a91ef78970a1f2e430a16a8f99bc2fc19cf2a5e6b6698fe9fb5e1d3025e76a8aR165-R174

More demos:

@aralroca
Copy link
Collaborator Author

aralroca commented May 20, 2024

@aralroca aralroca added the high label Jun 4, 2024
@aralroca
Copy link
Collaborator Author

Example of moveBefore:

const moveOrInsertNode = (container, node, ref_node = null) => {
 const canMove =  (container.isConnected && node.isConnected && (!ref_node || ref_node.parentNode === container);
  return canMove ? container.moveBefore(node, ref_node) : container.insertBefore(node, ref_node);
}

whatwg/dom#1307 (comment)

@ansarizafar
Copy link

We can also investigate how Svelte 5 is rendering lists with fine grained reactivity with array.push() https://svelte.dev/docs/svelte/$state#Deep-state

@aralroca
Copy link
Collaborator Author

It looks like Svelte is using a proxy. Using a proxy is another solution, not the best for performance but another one

@ansarizafar
Copy link

I think there is not a huge performance difference otherwise Svelte/Vue teams have not opted for it. I will go for proxy for much better DX then what we currently have

    list.value = [...list.value, formData.get('item') as string]

VS

let todos = $state([
	{
		done: false,
		text: 'add more todos'
	}
]);

todos[0].done = !todos[0].done;

todos.push({
	done: false,
	text: 'eat lunch'
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working high
Projects
None yet
Development

No branches or pull requests

2 participants