mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
React.warn() and React.error() (#15170)
This commit is contained in:
@@ -45,6 +45,7 @@ import {
|
||||
cloneElementWithValidation,
|
||||
} from './ReactElementValidator';
|
||||
import ReactSharedInternals from './ReactSharedInternals';
|
||||
import {error, warn} from './withComponentStack';
|
||||
import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
const React = {
|
||||
@@ -65,6 +66,9 @@ const React = {
|
||||
lazy,
|
||||
memo,
|
||||
|
||||
error,
|
||||
warn,
|
||||
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function normalizeCodeLocInfo(str) {
|
||||
return str && str.replace(/at .+?:\d+/g, 'at **');
|
||||
}
|
||||
|
||||
function expectHelper(spy, prefix, ...expectedArgs) {
|
||||
const expectedStack = expectedArgs.pop();
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
|
||||
const actualArgs = spy.calls.mostRecent().args;
|
||||
|
||||
let actualStack = undefined;
|
||||
if (expectedStack !== undefined) {
|
||||
actualStack = actualArgs.pop();
|
||||
expect(normalizeCodeLocInfo(actualStack)).toBe(expectedStack);
|
||||
}
|
||||
|
||||
expect(actualArgs).toHaveLength(expectedArgs.length);
|
||||
actualArgs.forEach((actualArg, index) => {
|
||||
const expectedArg = expectedArgs[index];
|
||||
expect(actualArg).toBe(
|
||||
index === 0 ? `${prefix}: ${expectedArg}` : expectedArg,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function expectMessageAndStack(...expectedArgs) {
|
||||
expectHelper(console.error, 'error', ...expectedArgs);
|
||||
expectHelper(console.warn, 'warn', ...expectedArgs);
|
||||
}
|
||||
|
||||
describe('withComponentStack', () => {
|
||||
let React = null;
|
||||
let ReactTestRenderer = null;
|
||||
let error = null;
|
||||
let scheduler = null;
|
||||
let warn = null;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.mock('scheduler', () => require('scheduler/unstable_mock'));
|
||||
|
||||
React = require('react');
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
scheduler = require('scheduler');
|
||||
|
||||
error = React.error;
|
||||
warn = React.warn;
|
||||
|
||||
spyOnDevAndProd(console, 'error');
|
||||
spyOnDevAndProd(console, 'warn');
|
||||
});
|
||||
|
||||
if (!__DEV__) {
|
||||
it('does nothing in production mode', () => {
|
||||
error('error');
|
||||
warn('warning');
|
||||
|
||||
expect(console.error).toHaveBeenCalledTimes(0);
|
||||
expect(console.warn).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
it('does not include component stack when called outside of render', () => {
|
||||
error('error: logged outside of render');
|
||||
warn('warn: logged outside of render');
|
||||
expectMessageAndStack('logged outside of render', undefined);
|
||||
});
|
||||
|
||||
it('should support multiple args', () => {
|
||||
function Component() {
|
||||
error('error: number:', 123, 'boolean:', true);
|
||||
warn('warn: number:', 123, 'boolean:', true);
|
||||
return null;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(<Component />);
|
||||
|
||||
expectMessageAndStack(
|
||||
'number:',
|
||||
123,
|
||||
'boolean:',
|
||||
true,
|
||||
'\n in Component (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('includes component stack when called from a render method', () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
return <Child />;
|
||||
}
|
||||
}
|
||||
|
||||
function Child() {
|
||||
error('error: logged in child render method');
|
||||
warn('warn: logged in child render method');
|
||||
return null;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(<Parent />);
|
||||
|
||||
expectMessageAndStack(
|
||||
'logged in child render method',
|
||||
'\n in Child (at **)' + '\n in Parent (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('includes component stack when called from a render phase lifecycle method', () => {
|
||||
function Parent() {
|
||||
return <Child />;
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
UNSAFE_componentWillMount() {
|
||||
error('error: logged in child cWM lifecycle');
|
||||
warn('warn: logged in child cWM lifecycle');
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(<Parent />);
|
||||
|
||||
expectMessageAndStack(
|
||||
'logged in child cWM lifecycle',
|
||||
'\n in Child (at **)' + '\n in Parent (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('includes component stack when called from a commit phase lifecycle method', () => {
|
||||
function Parent() {
|
||||
return <Child />;
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
componentDidMount() {
|
||||
error('error: logged in child cDM lifecycle');
|
||||
warn('warn: logged in child cDM lifecycle');
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(<Parent />);
|
||||
|
||||
expectMessageAndStack(
|
||||
'logged in child cDM lifecycle',
|
||||
'\n in Child (at **)' + '\n in Parent (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('includes component stack when called from a passive effect handler', () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
return <Child />;
|
||||
}
|
||||
}
|
||||
|
||||
function Child() {
|
||||
React.useEffect(() => {
|
||||
error('error: logged in child render method');
|
||||
warn('warn: logged in child render method');
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(<Parent />);
|
||||
|
||||
scheduler.flushAll(); // Flush passive effects
|
||||
|
||||
expectMessageAndStack(
|
||||
'logged in child render method',
|
||||
'\n in Child (at **)' + '\n in Parent (at **)',
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
||||
function noop() {}
|
||||
|
||||
let error = noop;
|
||||
let warn = noop;
|
||||
if (__DEV__) {
|
||||
const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
||||
|
||||
error = function() {
|
||||
const stack = ReactDebugCurrentFrame.getStackAddendum();
|
||||
if (stack !== '') {
|
||||
const length = arguments.length;
|
||||
const args = new Array(length + 1);
|
||||
for (let i = 0; i < length; i++) {
|
||||
args[i] = arguments[i];
|
||||
}
|
||||
args[length] = stack;
|
||||
console.error.apply(console, args);
|
||||
} else {
|
||||
console.error.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
warn = function() {
|
||||
const stack = ReactDebugCurrentFrame.getStackAddendum();
|
||||
if (stack !== '') {
|
||||
const length = arguments.length;
|
||||
const args = new Array(length + 1);
|
||||
for (let i = 0; i < length; i++) {
|
||||
args[i] = arguments[i];
|
||||
}
|
||||
args[length] = stack;
|
||||
console.warn.apply(console, args);
|
||||
} else {
|
||||
console.warn.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export {error, warn};
|
||||
Reference in New Issue
Block a user