Skip to content

Commit 846fce8

Browse files
authored
Fix Style updates not properly cleaning previous subscriptions (#11)
* Adds unit tests that checks that indeed we are adding more subscriptions to style Facets * Adds new test that verifies we are subscribing when updating to a new facet * Check if the value has changed before subscribing again * Adds a couple more unit tests o updating styles * Rename the tests
1 parent 09f2730 commit 846fce8

File tree

2 files changed

+217
-13
lines changed

2 files changed

+217
-13
lines changed

packages/@react-facet/dom-fiber/src/setupHostConfig.spec.tsx

Lines changed: 203 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { createFacet } from '@react-facet/core'
1+
import { createFacet, Facet } from '@react-facet/core'
22
import React, { ReactElement, useEffect, useState } from 'react'
3+
import { Fiber } from 'react-reconciler'
34
import { createFiberRoot } from './createFiberRoot'
45
import { createReconciler } from './createReconciler'
5-
import { InputType } from './types'
6+
import { setupHostConfig } from './setupHostConfig'
7+
import { InputType, ElementContainer, ElementProps, Props } from './types'
68

79
document.body.innerHTML = `<div id="root"></div>`
810

@@ -898,3 +900,202 @@ describe('update', () => {
898900
})
899901
})
900902
})
903+
904+
describe('commitUpdate style prop', () => {
905+
it('subscribes when updating from null', () => {
906+
const hostConfig = setupHostConfig()
907+
const instance: ElementContainer = {
908+
element: document.createElement('div'),
909+
styleUnsubscribers: new Map(),
910+
}
911+
912+
const oldProps: ElementProps<HTMLDivElement> = {
913+
style: {
914+
color: undefined,
915+
},
916+
}
917+
918+
const newColorFacet: Facet<string> = {
919+
get: () => 'blue',
920+
observe: jest.fn(),
921+
}
922+
923+
const newProps: ElementProps<HTMLDivElement> = {
924+
style: {
925+
color: newColorFacet,
926+
},
927+
}
928+
929+
hostConfig.commitUpdate?.(
930+
instance,
931+
true,
932+
'fast-div',
933+
oldProps as Props<HTMLDivElement>,
934+
newProps as Props<HTMLDivElement>,
935+
null as unknown as Fiber,
936+
)
937+
938+
// Adds a new subscription to the new Facet
939+
expect(newColorFacet.observe).toHaveBeenCalledTimes(1)
940+
})
941+
942+
it('unsubscribes when updating to null', () => {
943+
const colorUnsubscriber = jest.fn()
944+
945+
const hostConfig = setupHostConfig()
946+
const instance: ElementContainer = {
947+
element: document.createElement('div'),
948+
styleUnsubscribers: new Map([['color', colorUnsubscriber]]),
949+
}
950+
951+
const oldColorFacet: Facet<string> = {
952+
get: () => 'blue',
953+
observe: jest.fn(),
954+
}
955+
956+
const oldProps: ElementProps<HTMLDivElement> = {
957+
style: {
958+
color: oldColorFacet,
959+
},
960+
}
961+
962+
const newProps: ElementProps<HTMLDivElement> = {
963+
style: {
964+
color: undefined,
965+
},
966+
}
967+
968+
hostConfig.commitUpdate?.(
969+
instance,
970+
true,
971+
'fast-div',
972+
oldProps as Props<HTMLDivElement>,
973+
newProps as Props<HTMLDivElement>,
974+
null as unknown as Fiber,
975+
)
976+
977+
expect(colorUnsubscriber).toHaveBeenCalledTimes(1)
978+
})
979+
980+
it('unsubscribes from previous facet when changing to a primitive value', () => {
981+
const colorUnsubscriber = jest.fn()
982+
983+
const hostConfig = setupHostConfig()
984+
const instance: ElementContainer = {
985+
element: document.createElement('div'),
986+
styleUnsubscribers: new Map([['color', colorUnsubscriber]]),
987+
}
988+
989+
const oldProps: ElementProps<HTMLDivElement> = {
990+
style: {
991+
color: createFacet({ initialValue: 'blue' }),
992+
},
993+
}
994+
995+
const newProps: ElementProps<HTMLDivElement> = {
996+
style: {
997+
color: 'yellow',
998+
},
999+
}
1000+
1001+
hostConfig.commitUpdate?.(
1002+
instance,
1003+
true,
1004+
'fast-div',
1005+
oldProps as Props<HTMLDivElement>,
1006+
newProps as Props<HTMLDivElement>,
1007+
null as unknown as Fiber,
1008+
)
1009+
1010+
expect(colorUnsubscriber).toHaveBeenCalledTimes(1)
1011+
})
1012+
1013+
it('unsubscribes from previous facet when changing to a new facet', () => {
1014+
const colorUnsubscriber = jest.fn()
1015+
1016+
const hostConfig = setupHostConfig()
1017+
const instance: ElementContainer = {
1018+
element: document.createElement('div'),
1019+
styleUnsubscribers: new Map([['color', colorUnsubscriber]]),
1020+
}
1021+
1022+
const oldColorFacet: Facet<string> = {
1023+
get: () => 'blue',
1024+
observe: jest.fn(),
1025+
}
1026+
1027+
const oldProps: ElementProps<HTMLDivElement> = {
1028+
style: {
1029+
color: oldColorFacet,
1030+
},
1031+
}
1032+
1033+
const newColorFacet: Facet<string> = {
1034+
get: () => 'blue',
1035+
observe: jest.fn(),
1036+
}
1037+
1038+
const newProps: ElementProps<HTMLDivElement> = {
1039+
style: {
1040+
color: newColorFacet,
1041+
},
1042+
}
1043+
1044+
hostConfig.commitUpdate?.(
1045+
instance,
1046+
true,
1047+
'fast-div',
1048+
oldProps as Props<HTMLDivElement>,
1049+
newProps as Props<HTMLDivElement>,
1050+
null as unknown as Fiber,
1051+
)
1052+
1053+
// Unsubscribes from the old subscription, since it is a new Facet
1054+
expect(colorUnsubscriber).toHaveBeenCalledTimes(1)
1055+
1056+
// Adds a new subscription to the new Facet
1057+
expect(newColorFacet.observe).toHaveBeenCalledTimes(1)
1058+
})
1059+
1060+
it('keeps the same subscription when updating with the same facet', () => {
1061+
const colorUnsubscriber = jest.fn()
1062+
1063+
const hostConfig = setupHostConfig()
1064+
const instance: ElementContainer = {
1065+
element: document.createElement('div'),
1066+
styleUnsubscribers: new Map([['color', colorUnsubscriber]]),
1067+
}
1068+
1069+
const colorFacet: Facet<string> = {
1070+
get: () => 'blue',
1071+
observe: jest.fn(),
1072+
}
1073+
1074+
const oldProps: ElementProps<HTMLDivElement> = {
1075+
style: {
1076+
color: colorFacet,
1077+
},
1078+
}
1079+
1080+
const newProps: ElementProps<HTMLDivElement> = {
1081+
style: {
1082+
color: colorFacet,
1083+
},
1084+
}
1085+
1086+
hostConfig.commitUpdate?.(
1087+
instance,
1088+
true,
1089+
'fast-div',
1090+
oldProps as Props<HTMLDivElement>,
1091+
newProps as Props<HTMLDivElement>,
1092+
null as unknown as Fiber,
1093+
)
1094+
1095+
// I shouldn't unsubscribe, since it is the same Facet
1096+
expect(colorUnsubscriber).not.toHaveBeenCalled()
1097+
1098+
// So I must not also observe again, since I should stick with the previous subscription
1099+
expect(colorFacet.observe).toHaveBeenCalledTimes(0)
1100+
})
1101+
})

packages/@react-facet/dom-fiber/src/setupHostConfig.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -279,17 +279,20 @@ export const setupHostConfig = (): HostConfig<
279279

280280
if (newStyleProp != null) {
281281
for (const key in newStyleProp) {
282-
const value = newProps.style?.[key]
283-
284-
if (isFacet(value)) {
285-
styleUnsubscribers.set(
286-
key,
287-
value.observe((value) => {
288-
notNullStyle[key] = value
289-
}),
290-
)
291-
} else {
292-
notNullStyle[key] = value
282+
const oldValue = oldStyleProp?.[key]
283+
const newValue = newStyleProp[key]
284+
285+
if (oldValue !== newValue || oldStyleProp == null) {
286+
if (isFacet(newValue)) {
287+
styleUnsubscribers.set(
288+
key,
289+
newValue.observe((value) => {
290+
notNullStyle[key] = value
291+
}),
292+
)
293+
} else {
294+
notNullStyle[key] = newValue
295+
}
293296
}
294297
}
295298
}

0 commit comments

Comments
 (0)