Skip to content

Commit 26f605c

Browse files
committed
fix: infinite rerenders when settings props
1 parent ae59273 commit 26f605c

9 files changed

Lines changed: 267 additions & 229 deletions

File tree

cpp/HybridStyleRegistry.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace margelo::nitro::cssnitro {
3030
// Initialize static members
3131
std::unique_ptr<ShadowTreeUpdateManager> HybridStyleRegistry::shadowUpdates_ =
3232
std::make_unique<ShadowTreeUpdateManager>();
33-
std::unordered_map<std::string, std::shared_ptr<reactnativecss::Computed<Styled>>> HybridStyleRegistry::computedMap_;
33+
std::unordered_map<std::string, std::shared_ptr<reactnativecss::Computed<Styled *>>> HybridStyleRegistry::computedMap_;
3434
std::unordered_map<std::string, std::shared_ptr<reactnativecss::Observable<std::vector<HybridStyleRule>>>> HybridStyleRegistry::styleRuleMap_;
3535
std::atomic<uint64_t> HybridStyleRegistry::nextStyleRuleId_{1};
3636

@@ -206,7 +206,16 @@ namespace margelo::nitro::cssnitro {
206206

207207
computedMap_[componentId] = computed;
208208

209-
return computed->get();
209+
// Get the value from computed - it's a Styled* that may be nullptr
210+
Styled *styledPtr = computed->get();
211+
212+
// If nullptr, return empty Styled{}
213+
if (styledPtr == nullptr) {
214+
return Styled{};
215+
}
216+
217+
// Otherwise dereference and return the value
218+
return *styledPtr;
210219
}
211220

212221
void HybridStyleRegistry::deregisterComponent(const std::string &componentId) {

cpp/HybridStyleRegistry.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ namespace margelo::nitro::cssnitro {
8787

8888
// Static shared state
8989
static std::unique_ptr<ShadowTreeUpdateManager> shadowUpdates_;
90-
static std::unordered_map<std::string, std::shared_ptr<reactnativecss::Computed<Styled>>> computedMap_;
90+
static std::unordered_map<std::string, std::shared_ptr<reactnativecss::Computed<Styled *>>> computedMap_;
9191
static std::unordered_map<std::string, std::shared_ptr<reactnativecss::Observable<std::vector<HybridStyleRule>>>> styleRuleMap_;
9292
static std::atomic<uint64_t> nextStyleRuleId_;
9393
};

cpp/StyledComputedFactory.cpp

Lines changed: 178 additions & 150 deletions
Large diffs are not rendered by default.

cpp/StyledComputedFactory.hpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,37 @@
1212

1313
namespace margelo::nitro::cssnitro {
1414

15-
class StyledComputedFactory;
15+
class StyledComputedFactory {
16+
public:
17+
/**
18+
* Convert an unordered_map to AnyMap with optional transform property handling.
19+
* @param mergedMap The source map to convert
20+
* @param applyTransformMapping If true, applies special handling for transform properties
21+
* @return The converted AnyMap
22+
*/
23+
static std::shared_ptr<margelo::nitro::AnyMap> convertToAnyMap(
24+
const std::unordered_map<std::string, margelo::nitro::AnyValue> &mergedMap,
25+
bool applyTransformMapping);
1626

17-
// Build a Computed<Styled> that resolves styles from classNames against the styleRuleMap
27+
/**
28+
* Process declarations from a style rule and merge them into target maps.
29+
* @param declarations The variant containing style/prop declarations
30+
* @param targetStyles The map to store style declarations
31+
* @param targetProps The map to store prop declarations
32+
* @param get The Effect GetProxy for resolving reactive values
33+
* @param variableScope The scope for variable resolution
34+
*/
35+
static void processDeclarations(
36+
const auto &declarations,
37+
std::unordered_map<std::string, margelo::nitro::AnyValue> &targetStyles,
38+
std::unordered_map<std::string, margelo::nitro::AnyValue> &targetProps,
39+
reactnativecss::Effect::GetProxy &get,
40+
const std::string &variableScope);
41+
};
42+
43+
// Build a Computed<Styled*> that resolves styles from classNames against the styleRuleMap
1844
// and notifies ShadowTreeUpdateManager with the value of next.style for the given componentId.
19-
std::shared_ptr<reactnativecss::Computed<Styled>> makeStyledComputed(
45+
std::shared_ptr<reactnativecss::Computed<Styled *>> makeStyledComputed(
2046
const std::unordered_map<std::string, std::shared_ptr<reactnativecss::Observable<std::vector<HybridStyleRule>>>> &styleRuleMap,
2147
const std::string &classNames,
2248
const std::string &componentId,

example/src/App.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ StyleRegistry.addStyleSheet({
99
"text-red-500",
1010
[
1111
{
12-
s: specificity({ className: 1 }),
12+
s: specificity({ className: 1, important: 1 }),
13+
d: [
14+
{
15+
color: "green",
16+
},
17+
],
18+
},
19+
{
20+
s: specificity({ className: 2 }),
1321
d: [
1422
{
1523
color: "red",
@@ -40,7 +48,7 @@ export default function App() {
4048
return (
4149
<View style={styles.container}>
4250
<Text className="text-red-500" style={{ fontSize: 30 }} selectable>
43-
Multiply: {multiply(3, 7)}
51+
Multiply17: {multiply(3, 7)}
4452
</Text>
4553
</View>
4654
);

src/components/Text/Text.native.tsx

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import { Text as RNText } from "react-native";
33

44
import { createAnimatedComponent } from "react-native-reanimated";
55

6-
import { useElement } from "../../native/useElement";
7-
import { useDualRefs } from "../../native/useRef";
86
import { useStyledProps } from "../../native/useStyled";
9-
import { StyleRegistry } from "../../specs/StyleRegistry";
10-
import { copyComponentProperties, getDeepKeys } from "../../utils";
7+
// import { useDualRefs } from "../../native/useRef";
8+
import { copyComponentProperties } from "../../utils";
119

1210
const AnimatedText = createAnimatedComponent(RNText);
1311

@@ -17,25 +15,28 @@ export const Text = copyComponentProperties(
1715
const componentId = useId();
1816
const styled = useStyledProps(componentId, p.className, p);
1917

20-
const ref = useDualRefs(componentId, p.ref);
21-
22-
if (p.style) {
23-
StyleRegistry.updateComponentInlineStyleKeys(
24-
componentId,
25-
getDeepKeys(p.style),
26-
);
27-
}
28-
29-
return useElement(AnimatedText, styled, {
30-
...styled.props,
31-
...p,
32-
...styled.importantProps,
33-
ref,
34-
style:
35-
styled.style || styled.importantStyle
36-
? [styled.style, p.style, styled.importantStyle]
37-
: p.style,
38-
});
18+
// // const ref = useDualRefs(componentId, p.ref);
19+
20+
// if (p.style) {
21+
// StyleRegistry.updateComponentInlineStyleKeys(
22+
// componentId,
23+
// getDeepKeys(p.style),
24+
// );
25+
// }
26+
27+
// return useElement(AnimatedText, styled, {
28+
// ...styled.props,
29+
// ...p,
30+
// ...styled.importantProps,
31+
// // ref,
32+
// style:
33+
// styled.style || styled.importantStyle
34+
// ? [styled.style, p.style, styled.importantStyle]
35+
// : p.style,
36+
// });
37+
console.log("render", styled);
38+
39+
return <AnimatedText {...p} />;
3940
},
4041
);
4142

src/components/Text/Text.tsx

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,12 @@
1-
import { useId, type ComponentProps, type Ref } from "react";
2-
import { Text as RNText } from "react-native";
1+
import { Text as RNText, type TextProps } from "react-native";
32

4-
import { createAnimatedComponent } from "react-native-reanimated";
5-
6-
import { useElement } from "../../native/useElement";
7-
import { useDualRefs } from "../../native/useRef";
8-
import { useStyledProps } from "../../native/useStyled";
9-
import { StyleRegistry } from "../../specs/StyleRegistry";
10-
import { copyComponentProperties, getDeepKeys } from "../../utils";
11-
12-
const AnimatedText = createAnimatedComponent(RNText);
3+
import { copyComponentProperties } from "../../utils";
4+
import { useStyled } from "../../web/useStyled";
135

146
export const Text = copyComponentProperties(
15-
AnimatedText,
16-
(
17-
p: ComponentProps<typeof AnimatedText> & {
18-
className?: string;
19-
ref?: Ref<RNText>;
20-
},
21-
) => {
22-
const componentId = useId();
23-
const styled = useStyledProps(componentId, p.className, p);
24-
const ref = useDualRefs(componentId, p.ref);
25-
26-
if (p.style) {
27-
StyleRegistry.updateComponentInlineStyleKeys(
28-
componentId,
29-
getDeepKeys(p.style),
30-
);
31-
}
32-
33-
return useElement(AnimatedText, styled, {
34-
...styled.props,
35-
...p,
36-
...styled.importantProps,
37-
ref,
38-
style:
39-
styled.style || styled.importantStyle
40-
? [styled.style, p.style, styled.importantStyle]
41-
: p.style,
42-
});
7+
RNText,
8+
({ className, style, ...props }: TextProps & { className?: string }) => {
9+
const nextStyle = useStyled(className, style);
10+
return <RNText {...props} style={nextStyle} />;
4311
},
4412
);
45-
46-
export default Text;

src/native/specificity.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { SpecificityArray } from "../specs/StyleRegistry/HybridStyleRegistry.nitro";
22

33
const Specificity = {
4-
order: 0,
5-
className: 1,
6-
important: 2,
7-
inline: 3,
8-
pseudoElements: 4,
9-
pseudoClass: 1,
4+
important: 0,
5+
inline: 1,
6+
pseudoElements: 2,
7+
className: 3,
8+
pseudoClass: 3,
9+
order: 4,
1010
// Id: 0, - We don't support ID yet
1111
// StyleSheet: 0, - We don't support multiple stylesheets
1212
};

src/native/useStyled.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export function useStyledProps(
7676

7777
useEffect(
7878
() => () => {
79-
StyleRegistry.deregisterComponent(componentId);
79+
// StyleRegistry.deregisterComponent(componentId);
8080
},
8181
[componentId],
8282
);

0 commit comments

Comments
 (0)