Skip to content

Commit

Permalink
♻️ Cleans Up Entangle (#3792)
Browse files Browse the repository at this point in the history
* ♻️ Cleans Up entangle

* 🐛 Set hash on first run

* 🐛 Deeply clones entangled values

* 🐛 Actually fixes livewire regression
  • Loading branch information
ekwoka authored Oct 18, 2023
1 parent 0913cdb commit 3184106
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 35 deletions.
25 changes: 10 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 18 additions & 20 deletions packages/alpinejs/src/entangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,37 @@ import { effect, release } from './reactivity'

export function entangle({ get: outerGet, set: outerSet }, { get: innerGet, set: innerSet }) {
let firstRun = true
let outerHash, innerHash, outerHashLatest, innerHashLatest
let outerHash

let reference = effect(() => {
let outer, inner

const outer = outerGet()
const inner = innerGet()
if (firstRun) {
outer = outerGet()
innerSet(JSON.parse(JSON.stringify(outer))) // We need to break internal references using parse/stringify...
inner = innerGet()
innerSet(cloneIfObject(outer))
firstRun = false
outerHash = JSON.stringify(outer)
} else {
outer = outerGet()
inner = innerGet()

outerHashLatest = JSON.stringify(outer)
innerHashLatest = JSON.stringify(inner)
const outerHashLatest = JSON.stringify(outer)

if (outerHashLatest !== outerHash) { // If outer changed...
inner = innerGet()
innerSet(outer)
inner = outer // Assign inner to outer so that it can be serialized for diffing...
innerSet(cloneIfObject(outer))
outerHash = outerHashLatest
} else { // If inner changed...
outerSet(JSON.parse(innerHashLatest ?? null)) // We need to break internal references using parse/stringify...
outer = inner // Assign outer to inner so that it can be serialized for diffing...
outerSet(cloneIfObject(inner))
outerHash = JSON.stringify(inner)
}
}

// Re serialize values...
outerHash = JSON.stringify(outer)
innerHash = JSON.stringify(inner)
JSON.stringify(innerGet())
JSON.stringify(outerGet())
})

return () => {
release(reference)
}
}

function cloneIfObject(value) {
return typeof value === 'object'
? JSON.parse(JSON.stringify(value))
: value
}
35 changes: 35 additions & 0 deletions tests/cypress/integration/entangle.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,38 @@ test.skip('can release entanglement',
get('input[outer]').should(haveValue('foobar'))
}
)

test(
"can handle undefined",
[
html`
<div x-data="{ outer: undefined }">
<input x-model="outer" outer />
<div
x-data="{ inner: 'bar' }"
x-init="() => {}; Alpine.entangle(
{
get() { return outer },
set(value) { outer = value },
},
{
get() { return inner },
set(value) { inner = value },
}
)"
>
<input x-model="inner" inner />
</div>
</div>
`,
],
({ get }) => {
get("input[outer]").should(haveValue(''));
get("input[inner]").should(haveValue(''));

get("input[inner]").type("bar");
get("input[inner]").should(haveValue("bar"));
get("input[outer]").should(haveValue("bar"));
}
);

0 comments on commit 3184106

Please sign in to comment.