Skip to content

Commit 3184106

Browse files
authored
♻️ Cleans Up Entangle (#3792)
* ♻️ Cleans Up entangle * 🐛 Set hash on first run * 🐛 Deeply clones entangled values * 🐛 Actually fixes livewire regression
1 parent 0913cdb commit 3184106

File tree

3 files changed

+63
-35
lines changed

3 files changed

+63
-35
lines changed

package-lock.json

Lines changed: 10 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/alpinejs/src/entangle.js

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,37 @@ import { effect, release } from './reactivity'
22

33
export function entangle({ get: outerGet, set: outerSet }, { get: innerGet, set: innerSet }) {
44
let firstRun = true
5-
let outerHash, innerHash, outerHashLatest, innerHashLatest
5+
let outerHash
66

77
let reference = effect(() => {
8-
let outer, inner
9-
8+
const outer = outerGet()
9+
const inner = innerGet()
1010
if (firstRun) {
11-
outer = outerGet()
12-
innerSet(JSON.parse(JSON.stringify(outer))) // We need to break internal references using parse/stringify...
13-
inner = innerGet()
11+
innerSet(cloneIfObject(outer))
1412
firstRun = false
13+
outerHash = JSON.stringify(outer)
1514
} else {
16-
outer = outerGet()
17-
inner = innerGet()
18-
19-
outerHashLatest = JSON.stringify(outer)
20-
innerHashLatest = JSON.stringify(inner)
15+
const outerHashLatest = JSON.stringify(outer)
2116

2217
if (outerHashLatest !== outerHash) { // If outer changed...
23-
inner = innerGet()
24-
innerSet(outer)
25-
inner = outer // Assign inner to outer so that it can be serialized for diffing...
18+
innerSet(cloneIfObject(outer))
19+
outerHash = outerHashLatest
2620
} else { // If inner changed...
27-
outerSet(JSON.parse(innerHashLatest ?? null)) // We need to break internal references using parse/stringify...
28-
outer = inner // Assign outer to inner so that it can be serialized for diffing...
21+
outerSet(cloneIfObject(inner))
22+
outerHash = JSON.stringify(inner)
2923
}
3024
}
31-
32-
// Re serialize values...
33-
outerHash = JSON.stringify(outer)
34-
innerHash = JSON.stringify(inner)
25+
JSON.stringify(innerGet())
26+
JSON.stringify(outerGet())
3527
})
3628

3729
return () => {
3830
release(reference)
3931
}
4032
}
33+
34+
function cloneIfObject(value) {
35+
return typeof value === 'object'
36+
? JSON.parse(JSON.stringify(value))
37+
: value
38+
}

tests/cypress/integration/entangle.spec.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,38 @@ test.skip('can release entanglement',
6969
get('input[outer]').should(haveValue('foobar'))
7070
}
7171
)
72+
73+
test(
74+
"can handle undefined",
75+
[
76+
html`
77+
<div x-data="{ outer: undefined }">
78+
<input x-model="outer" outer />
79+
80+
<div
81+
x-data="{ inner: 'bar' }"
82+
x-init="() => {}; Alpine.entangle(
83+
{
84+
get() { return outer },
85+
set(value) { outer = value },
86+
},
87+
{
88+
get() { return inner },
89+
set(value) { inner = value },
90+
}
91+
)"
92+
>
93+
<input x-model="inner" inner />
94+
</div>
95+
</div>
96+
`,
97+
],
98+
({ get }) => {
99+
get("input[outer]").should(haveValue(''));
100+
get("input[inner]").should(haveValue(''));
101+
102+
get("input[inner]").type("bar");
103+
get("input[inner]").should(haveValue("bar"));
104+
get("input[outer]").should(haveValue("bar"));
105+
}
106+
);

0 commit comments

Comments
 (0)