未验证 提交 33902442 编写于 作者: P Phil Plückthun 提交者: GitHub

Add warning for components that are created dynamically (#2998)

* Add check for whether component is used dynamically

* Fix checkDynamicCreation call and dedup by message

* Add tests for dynamic creation warning
上级 581993b0
......@@ -3,6 +3,7 @@ import React, { useContext, useEffect, useRef } from 'react';
import { STATIC_EXECUTION_CONTEXT } from '../constants';
import GlobalStyle from '../models/GlobalStyle';
import { useStyleSheet, useStylis } from '../models/StyleSheetManager';
import { checkDynamicCreation } from '../utils/checkDynamicCreation';
import determineTheme from '../utils/determineTheme';
import { ThemeContext } from '../models/ThemeProvider';
import { EMPTY_ARRAY } from '../utils/empties';
......@@ -21,6 +22,10 @@ export default function createGlobalStyle(
const styledComponentId = `sc-global-${generateComponentId(JSON.stringify(rules))}`;
const globalStyle = new GlobalStyle(rules, styledComponentId);
if (process.env.NODE_ENV !== 'production') {
checkDynamicCreation(styledComponentId);
}
function GlobalStyleComponent(props: GlobalStyleComponentPropsType) {
const styleSheet = useStyleSheet();
const stylis = useStylis();
......
......@@ -11,6 +11,7 @@ import hoist from 'hoist-non-react-statics';
import merge from '../utils/mixinDeep';
import ComponentStyle from './ComponentStyle';
import createWarnTooManyClasses from '../utils/createWarnTooManyClasses';
import { checkDynamicCreation } from '../utils/checkDynamicCreation';
import determineTheme from '../utils/determineTheme';
import escape from '../utils/escape';
import generateDisplayName from '../utils/generateDisplayName';
......@@ -277,6 +278,8 @@ export default function createStyledComponent(
});
if (process.env.NODE_ENV !== 'production') {
checkDynamicCreation(displayName, styledComponentId);
WrappedStyledComponent.warnTooManyClasses = createWarnTooManyClasses(
displayName,
styledComponentId
......
// @flow
import React from 'react';
import TestRenderer from 'react-test-renderer';
import { resetStyled } from './utils';
describe('warns on dynamic creation', () => {
let warn;
let styled;
beforeEach(() => {
warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
styled = resetStyled();
});
afterEach(() => {
warn.mockReset();
});
it('should warn when a component was created dynamically', () => {
const Outer = () => {
const Inner = styled.div`
color: palevioletred;
`;
return <Inner />
};
TestRenderer.create(<Outer />);
expect(warn).toHaveBeenCalledTimes(1);
expect(warn.mock.calls[0][0]).toMatch(/has been created dynamically/i);
});
it('should warn only once for a given ID', () => {
const Outer = () => {
const Inner = styled.div.withConfig({
displayName: 'Inner',
componentId: 'Inner',
})`
color: palevioletred;
`;
return <Inner />
};
TestRenderer.create(<Outer />);
TestRenderer.create(<Outer />);
expect(warn).toHaveBeenCalledTimes(1);
});
it('should not warn in any other case', () => {
const Inner = styled.div`
color: palevioletred;
`;
const Outer = () => <Inner />;
TestRenderer.create(<Outer />);
expect(warn).toHaveBeenCalledTimes(0);
});
});
// @flow
import { useRef } from 'react';
const invalidHookCallRe = /invalid hook call/i;
const seen = new Set();
export const checkDynamicCreation = (displayName: string, componentId?: string) => {
if (process.env.NODE_ENV !== 'production') {
const parsedIdString = componentId ? ` with the id of "${componentId}"` : '';
const message =
`The component ${displayName}${parsedIdString} has been created dynamically.\n` +
'You may see this warning because you\'ve called styled inside another component.\n' +
'To resolve this only create new StyledComponents outside of any render method and function component.';
try {
// We purposefully call `useRef` outside of a component and expect it to throw
// If it doesn't, then we're inside another component.
// eslint-disable-next-line react-hooks/rules-of-hooks
useRef();
if (!seen.has(message)) {
// eslint-disable-next-line no-console
console.warn(message);
seen.add(message);
}
} catch (error) {
// The error here is expected, since we're expecting anything that uses `checkDynamicCreation` to
// be called outside of a React component.
if (invalidHookCallRe.test(error.message)) {
// This shouldn't happen, but resets `warningSeen` if we had this error happen intermittently
seen.delete(message);
}
}
}
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册