Skip to content

Improving feedback for "Text strings must be rendered within a <Text> component." errors. #20803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions packages/react-native-renderer/src/AssertTextIsInTextComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import invariant from 'shared/invariant';

export type HostContext = $ReadOnly<{|
isInAParentText: boolean,
|}>;

function stripInformation(internalInstanceHandle: Object) {
const possibleCause =
'\n\nProbably result of a conditional rendering using boolean concatination as in `cond && <Component ...>`.';
if (internalInstanceHandle && internalInstanceHandle.sibling) {
const debugOwner = internalInstanceHandle.sibling._debugOwner;
const debugSource = internalInstanceHandle.sibling._debugSource;
if (debugOwner && debugSource) {
const parentComponentName = debugOwner.type.name;
const siblingSource = `"${debugSource.fileName}" line ${debugSource.lineNumber}, column ${debugSource.columnNumber}`;
return ` Error may have occured in component <${parentComponentName}> near ${siblingSource}. ${possibleCause}`;
}
}
return possibleCause;
}

export function assertTextInTextComponent(
hostContext: HostContext,
text: string,
internalInstanceHandle: Object,
) {
invariant(
hostContext.isInAParentText,
'Text string "%s" must be rendered within a <Text> component.%s',
text,
stripInformation(internalInstanceHandle),
);
}
6 changes: 2 additions & 4 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ import type {

import {mountSafeCallback_NOT_REALLY_SAFE} from './NativeMethodsMixinUtils';
import {create, diff} from './ReactNativeAttributePayload';
import {assertTextInTextComponent} from './AssertTextIsInTextComponent';

import {enableNewReconciler} from 'shared/ReactFeatureFlags';
import invariant from 'shared/invariant';
@@ -250,10 +251,7 @@ export function createTextInstance(
hostContext: HostContext,
internalInstanceHandle: Object,
): TextInstance {
invariant(
hostContext.isInAParentText,
'Text strings must be rendered within a <Text> component.',
);
assertTextInTextComponent(hostContext, text, internalInstanceHandle);

const tag = nextReactTag;
nextReactTag += 2;
6 changes: 2 additions & 4 deletions packages/react-native-renderer/src/ReactNativeHostConfig.js
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';

import {DefaultLanePriority as DefaultLanePriority_old} from 'react-reconciler/src/ReactFiberLane.old';
import {DefaultLanePriority as DefaultLanePriority_new} from 'react-reconciler/src/ReactFiberLane.new';
import {assertTextInTextComponent} from './AssertTextIsInTextComponent';

const DefaultLanePriority = enableNewReconciler
? DefaultLanePriority_new
@@ -152,10 +153,7 @@ export function createTextInstance(
hostContext: HostContext,
internalInstanceHandle: Object,
): TextInstance {
invariant(
hostContext.isInAParentText,
'Text strings must be rendered within a <Text> component.',
);
assertTextInTextComponent(hostContext, text, internalInstanceHandle);

const tag = allocateTag();

Original file line number Diff line number Diff line change
@@ -625,7 +625,7 @@ describe('ReactFabric', () => {
}));

expect(() => ReactFabric.render(<View>this should warn</View>, 11)).toThrow(
'Text strings must be rendered within a <Text> component.',
'Text string "this should warn" must be rendered within a <Text> component.\n\nProbably result of a conditional rendering using boolean concatination as in `cond && <Component ...>`.',
);

expect(() =>
@@ -635,7 +635,9 @@ describe('ReactFabric', () => {
</Text>,
11,
),
).toThrow('Text strings must be rendered within a <Text> component.');
).toThrow(
'Text string "hi hello hi" must be rendered within a <Text> component.\n\nProbably result of a conditional rendering using boolean concatination as in `cond && <Component ...>`.',
);
});

it('should not throw for text inside of an indirect <Text> ancestor', () => {
Original file line number Diff line number Diff line change
@@ -488,7 +488,7 @@ describe('ReactNative', () => {
}));

expect(() => ReactNative.render(<View>this should warn</View>, 11)).toThrow(
'Text strings must be rendered within a <Text> component.',
'Text string "this should warn" must be rendered within a <Text> component.\n\nProbably result of a conditional rendering using boolean concatination as in `cond && <Component ...>`.',
);

expect(() =>
@@ -498,7 +498,9 @@ describe('ReactNative', () => {
</Text>,
11,
),
).toThrow('Text strings must be rendered within a <Text> component.');
).toThrow(
'Text string "hi hello hi" must be rendered within a <Text> component.\n\nProbably result of a conditional rendering using boolean concatination as in `cond && <Component ...>`.',
);
});

it('should not throw for text inside of an indirect <Text> ancestor', () => {
3 changes: 2 additions & 1 deletion scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
@@ -373,5 +373,6 @@
"382": "This query has received more parameters than the last time the same query was used. Always pass the exact number of parameters that the query needs.",
"383": "This query has received fewer parameters than the last time the same query was used. Always pass the exact number of parameters that the query needs.",
"384": "Refreshing the cache is not supported in Server Components.",
"385": "A mutable source was mutated while the %s component was rendering. This is not supported. Move any mutations into event handlers or effects."
"385": "A mutable source was mutated while the %s component was rendering. This is not supported. Move any mutations into event handlers or effects.",
"386": "Text string \"%s\" must be rendered within a <Text> component.%s"
}