mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Adds a new option to `react-dom/server` entrypoints.
`onHeaders: (headers: Headers) => void` (non node envs)
`onHeaders: (headers: { Link?: string }) => void` (node envs)
When any `renderTo...` or `prerender...` function is called and this
option is provided the supplied function will be called sometime on or
before completion of the render with some preload link headers.
When provided during a `renderTo...` the callback will usually be called
after the first pass at work. The idea here is we want to get a set of
headers to start the browser loading well before the shell is ready. We
don't wait for the shell because if we did we may as well send the
preloads as tags in the HTML.
When provided during a `prerender...` the callback will be called after
the entire prerender is complete. The idea here is we are not responding
to a live request and it is preferable to capture as much as possible
for preloading as Headers in case the prerender was unable to finish the
shell.
Currently the following resources are always preloaded as headers when
the option is provided
1. prefetchDNS and preconnects
2. font preloads
3. high priority image preloads
Additionally if we are providing headers when the shell is incomplete
(regardless of whether it is render or prerender) we will also include
any stylesheet Resources (ones with a precedence prop)
There is a second option `maxHeadersLength?: number` which allows you to
specify the maximum length of the header content in unicode code units.
This is what you get when you read the length property of a string in
javascript. It's improtant to note that this is not the same as the
utf-8 byte length when these headers are serialized in a Response. The
utf8 representation may be the same size, or larger but it will never be
smaller.
If you do not supply a `maxHeadersLength` we defaul to `2000`. This was
chosen as half the value of the max headers length supported by commonly
known web servers and CDNs. many browser and web server can support
significantly more headers than this so you can use this option to
increase the headers limit. You can also of course use it to be even
more conservative. Again it is important to keep in mind there is no
direct translation between the max length and the bytelength and so if
you want to stay under a certain byte length you need to be potentially
more aggressive in the maxHeadersLength you choose.
Conceptually `onHeaders` could be called more than once as new headers
are discovered however if we haven't started flushing yet but since most
APIs for the server including the web standard Response only allow you
to set headers once the current implementation will only call it one
time
180 lines
6.0 KiB
JavaScript
180 lines
6.0 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
/*
|
|
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
|
|
* and Temporal.* types. See https://github.com/facebook/react/pull/22064.
|
|
*
|
|
* The functions in this module will throw an easier-to-understand,
|
|
* easier-to-debug exception with a clear errors message message explaining the
|
|
* problem. (Instead of a confusing exception thrown inside the implementation
|
|
* of the `value` object).
|
|
*/
|
|
|
|
// $FlowFixMe[incompatible-return] only called in DEV, so void return is not possible.
|
|
function typeName(value: mixed): string {
|
|
if (__DEV__) {
|
|
// toStringTag is needed for namespaced types like Temporal.Instant
|
|
const hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag;
|
|
const type =
|
|
(hasToStringTag && (value: any)[Symbol.toStringTag]) ||
|
|
(value: any).constructor.name ||
|
|
'Object';
|
|
// $FlowFixMe[incompatible-return]
|
|
return type;
|
|
}
|
|
}
|
|
|
|
// $FlowFixMe[incompatible-return] only called in DEV, so void return is not possible.
|
|
function willCoercionThrow(value: mixed): boolean {
|
|
if (__DEV__) {
|
|
try {
|
|
testStringCoercion(value);
|
|
return false;
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function testStringCoercion(value: mixed) {
|
|
// If you ended up here by following an exception call stack, here's what's
|
|
// happened: you supplied an object or symbol value to React (as a prop, key,
|
|
// DOM attribute, CSS property, string ref, etc.) and when React tried to
|
|
// coerce it to a string using `'' + value`, an exception was thrown.
|
|
//
|
|
// The most common types that will cause this exception are `Symbol` instances
|
|
// and Temporal objects like `Temporal.Instant`. But any object that has a
|
|
// `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this
|
|
// exception. (Library authors do this to prevent users from using built-in
|
|
// numeric operators like `+` or comparison operators like `>=` because custom
|
|
// methods are needed to perform accurate arithmetic or comparison.)
|
|
//
|
|
// To fix the problem, coerce this object or symbol value to a string before
|
|
// passing it to React. The most reliable way is usually `String(value)`.
|
|
//
|
|
// To find which value is throwing, check the browser or debugger console.
|
|
// Before this exception was thrown, there should be `console.error` output
|
|
// that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the
|
|
// problem and how that type was used: key, atrribute, input value prop, etc.
|
|
// In most cases, this console output also shows the component and its
|
|
// ancestor components where the exception happened.
|
|
//
|
|
// eslint-disable-next-line react-internal/safe-string-coercion
|
|
return '' + (value: any);
|
|
}
|
|
|
|
export function checkAttributeStringCoercion(
|
|
value: mixed,
|
|
attributeName: string,
|
|
): void | string {
|
|
if (__DEV__) {
|
|
if (willCoercionThrow(value)) {
|
|
console.error(
|
|
'The provided `%s` attribute is an unsupported type %s.' +
|
|
' This value must be coerced to a string before using it here.',
|
|
attributeName,
|
|
typeName(value),
|
|
);
|
|
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function checkKeyStringCoercion(value: mixed): void | string {
|
|
if (__DEV__) {
|
|
if (willCoercionThrow(value)) {
|
|
console.error(
|
|
'The provided key is an unsupported type %s.' +
|
|
' This value must be coerced to a string before using it here.',
|
|
typeName(value),
|
|
);
|
|
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function checkPropStringCoercion(
|
|
value: mixed,
|
|
propName: string,
|
|
): void | string {
|
|
if (__DEV__) {
|
|
if (willCoercionThrow(value)) {
|
|
console.error(
|
|
'The provided `%s` prop is an unsupported type %s.' +
|
|
' This value must be coerced to a string before using it here.',
|
|
propName,
|
|
typeName(value),
|
|
);
|
|
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function checkOptionStringCoercion(
|
|
value: mixed,
|
|
propName: string,
|
|
): void | string {
|
|
if (__DEV__) {
|
|
if (willCoercionThrow(value)) {
|
|
console.error(
|
|
'The provided `%s` option is an unsupported type %s.' +
|
|
' This value must be coerced to a string before using it here.',
|
|
propName,
|
|
typeName(value),
|
|
);
|
|
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function checkCSSPropertyStringCoercion(
|
|
value: mixed,
|
|
propName: string,
|
|
): void | string {
|
|
if (__DEV__) {
|
|
if (willCoercionThrow(value)) {
|
|
console.error(
|
|
'The provided `%s` CSS property is an unsupported type %s.' +
|
|
' This value must be coerced to a string before using it here.',
|
|
propName,
|
|
typeName(value),
|
|
);
|
|
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function checkHtmlStringCoercion(value: mixed): void | string {
|
|
if (__DEV__) {
|
|
if (willCoercionThrow(value)) {
|
|
console.error(
|
|
'The provided HTML markup uses a value of unsupported type %s.' +
|
|
' This value must be coerced to a string before using it here.',
|
|
typeName(value),
|
|
);
|
|
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function checkFormFieldValueStringCoercion(value: mixed): void | string {
|
|
if (__DEV__) {
|
|
if (willCoercionThrow(value)) {
|
|
console.error(
|
|
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
|
|
' must be strings, not %s.' +
|
|
' This value must be coerced to a string before using it here.',
|
|
typeName(value),
|
|
);
|
|
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|
}
|
|
}
|
|
}
|