Skip to content

Commit d1f6963

Browse files
committed
feat: add will-change-* classes
1 parent 0e88d01 commit d1f6963

File tree

4 files changed

+95
-42
lines changed

4 files changed

+95
-42
lines changed

example/src/App.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,8 @@ import "../global.css";
55
export default function App() {
66
return (
77
<>
8-
<View className="justify-center items-center flex-1 bg-linear-to-r from-cyan-500 to-blue-500">
9-
<Text className="">Test Component</Text>
10-
</View>
11-
<View
12-
className="justify-center items-center flex-1"
13-
style={{
14-
experimental_backgroundImage: "linear-gradient(to right, #f00, #0f0)",
15-
}}
16-
>
17-
<Text className="">Test Component2</Text>
8+
<View className="justify-center items-center h-full">
9+
<Text className="shadow-red-500 shadow-2xl">Test Component</Text>
1810
</View>
1911
</>
2012
);

src/runtime/native/__tests__/upgrading.test.tsx

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,17 @@ import { registerCSS, render, screen } from "react-native-css/jest";
55
const parentID = "parent";
66
const childID = "child";
77

8-
test("group", () => {
8+
const log = jest.fn();
9+
10+
beforeAll(() => {
11+
jest.spyOn(console, "log").mockImplementation(log);
12+
});
13+
14+
beforeEach(() => {
15+
log.mockClear();
16+
});
17+
18+
test("adding a group", () => {
919
registerCSS(
1020
`.group .my-class {
1121
color: red;
@@ -22,13 +32,45 @@ test("group", () => {
2232

2333
expect(child.props.style).toStrictEqual(undefined);
2434

25-
expect(() => {
26-
screen.rerender(
27-
<View testID={parentID} className="group">
28-
<Text testID={childID} className="my-class" />
29-
</View>,
30-
);
31-
})
32-
.toThrow(`ReactNativeCss: Cannot dynamically add a container context. 'group' was added after the initial render.
33-
Use modifier ('hover:container', 'active:container', etc) to ensure it present in the initial render`);
35+
screen.rerender(
36+
<View testID={parentID} className="group">
37+
<Text testID={childID} className="my-class" />
38+
</View>,
39+
);
40+
41+
expect(log.mock.calls).toEqual([
42+
[
43+
"ReactNativeCss: className 'group' added a container after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-container' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.",
44+
],
45+
]);
46+
});
47+
48+
test.only("will-change-container", () => {
49+
registerCSS(
50+
`.group .my-class {
51+
color: red;
52+
}`,
53+
);
54+
55+
render(
56+
<View testID={parentID} className="will-change-container">
57+
<Text testID={childID} className="my-class" />
58+
</View>,
59+
);
60+
61+
const child = screen.getByTestId(childID);
62+
63+
expect(child.props.style).toStrictEqual(undefined);
64+
65+
screen.rerender(
66+
<View testID={parentID} className="group">
67+
<Text testID={childID} className="my-class" />
68+
</View>,
69+
);
70+
71+
expect(log.mock.calls).toEqual([
72+
[
73+
"ReactNativeCss: className 'group' added a container after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-container' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.",
74+
],
75+
]);
3476
});

src/runtime/native/injection.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,33 @@ function isDeepEqual(a: unknown, b: unknown): boolean {
9999

100100
return true;
101101
}
102+
103+
StyleCollection.styles("will-change-variable").set([
104+
{
105+
s: [0],
106+
v: [],
107+
},
108+
]);
109+
110+
StyleCollection.styles("will-change-container").set([
111+
{
112+
s: [0],
113+
c: [],
114+
},
115+
]);
116+
117+
StyleCollection.styles("will-change-animation").set([
118+
{
119+
s: [0],
120+
a: true,
121+
},
122+
]);
123+
124+
StyleCollection.styles("will-change-pressable").set([
125+
{
126+
s: [0],
127+
p: {
128+
h: 1,
129+
},
130+
},
131+
]);

src/runtime/native/react/rules.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -166,31 +166,20 @@ export function updateRules(
166166
let pressable = activeFamily.has(state.ruleEffect);
167167

168168
if (Boolean(variables) !== Boolean(state.variables)) {
169-
throw new Error(
170-
`ReactNativeCss: Cannot dynamically add a variable context. '${source}' was added after the initial render.
171-
Use modifier ('hover:my-var', 'active:my-var', etc) to ensure it present in the initial render`,
169+
console.log(
170+
`ReactNativeCss: className '${source}' added a variable after the initial render. This causes the components state to be reset and all children be re-mounted. Use the className 'will-change-variable' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.`,
172171
);
173-
}
174-
175-
if (Boolean(containers) !== Boolean(state.containers)) {
176-
throw new Error(
177-
`ReactNativeCss: Cannot dynamically add a container context. '${source}' was added after the initial render.
178-
Use modifier ('hover:container', 'active:container', etc) to ensure it present in the initial render`,
172+
} else if (Boolean(containers) !== Boolean(state.containers)) {
173+
console.log(
174+
`ReactNativeCss: className '${source}' added a container after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-container' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.`,
179175
);
180-
}
181-
182-
if (animated !== state.animated) {
183-
throw new Error(
184-
`ReactNativeCss: Cannot dynamically change to an animated component. '${source}' was added after the initial render.
185-
Use 'animation-none' or a modifier ('hover:animation', 'active:animation', etc) to ensure it present in the initial render`,
176+
} else if (animated !== state.animated) {
177+
console.log(
178+
`ReactNativeCss: className '${source}' added an animation after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-animation' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.`,
186179
);
187-
}
188-
189-
if (pressable !== state.pressable) {
190-
throw new Error(
191-
`ReactNativeCss: Cannot dynamically change to a Pressable. '${source}' was added after the initial render.
192-
The 'hover', 'active', and 'focus' modifiers on a View will convert it to a Pressable.
193-
Use a modifier) to ensure it present in the initial render`,
180+
} else if (pressable !== state.pressable) {
181+
console.log(
182+
`ReactNativeCss: className '${source}' added a pressable state after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-pressable' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.`,
194183
);
195184
}
196185
}

0 commit comments

Comments
 (0)