8000 feat(react): Add useProfiler hook (#2659) · leesx/sentry-javascript@026dfe9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 026dfe9

Browse files
authored
feat(react): Add useProfiler hook (getsentry#2659)
1 parent 47b654c commit 026dfe9

File tree

7 files changed

+157
-33
lines changed

7 files changed

+157
-33
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66
- [react] feat: Add @sentry/react package (#2631)
77
- [react] feat: Add Error Boundary component (#2647)
8+
- [react] feat: Add useProfiler hook (#2659)
89

910
## 5.17.0
1011

packages/react/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
},
2929
"devDependencies": {
3030
"@testing-library/react": "^10.0.6",
31+
"@testing-library/react-hooks": "^3.3.0",
3132
"@types/hoist-non-react-statics": "^3.3.1",
3233
"@types/react": "^16.9.35",
3334
"jest": "^24.7.1",
@@ -36,9 +37,11 @@
3637
"prettier-check": "^2.0.0",
3738
"react": "^16.0.0",
3839
"react-dom": "^16.0.0",
40+
"react-test-renderer": "^16.13.1",
3941
"rimraf": "^2.6.3",
4042
"tslint": "^5.16.0",
4143
"tslint-react": "^5.0.0",
44+
"tslint-react-hooks": "^2.2.2",
4245
"typescript": "^3.5.1"
4346
},
4447
"scripts": {

packages/react/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export * from '@sentry/browser';
22

3-
export { Profiler, withProfiler } from './profiler';
3+
export { Profiler, withProfiler, useProfiler } from './profiler';
44
export { ErrorBoundary, withErrorBoundary } from './errorboundary';

packages/react/src/profiler.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,25 @@ function withProfiler<P extends object>(WrappedComponent: React.ComponentType<P>
111111
return Wrapped;
112112
}
113113

114-
export { withProfiler, Profiler };
114+
/**
115+
*
116+
* `useProfiler` is a React hook that profiles a React component.
117+
*
118+
* Requires React 16.8 or above.
119+
* @param name displayName of component being profiled
120+
*/
121+
function useProfiler(name: string): void {
122+
const activity = getInitActivity(name);
123+
124+
React.useEffect(() => {
125+
afterNextFrame(() => {
126+
const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER);
127+
if (tracingIntegration !== null) {
128+
// tslint:disable-next-line:no-unsafe-any
129+
(tracingIntegration as any).constructor.popActivity(activity);
130+
}
131+
});
132+
}, []);
133+
}
134+
135+
export { withProfiler, Profiler, useProfiler };

packages/react/test/profiler.test.tsx

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { render } from '@testing-library/react';
2+
import { renderHook } from '@testing-library/react-hooks';
23
import * as React from 'react';
34

4-
import { UNKNOWN_COMPONENT, withProfiler } from '../src/profiler';
5+
import { UNKNOWN_COMPONENT, useProfiler, withProfiler } from '../src/profiler';
56

67
const mockPushActivity = jest.fn().mockReturnValue(1);
78
const mockPopActivity = jest.fn();
@@ -25,46 +26,73 @@ jest.mock('@sentry/browser', () => ({
2526
}));
2627

2728
describe('withProfiler', () => {
29+
beforeEach(() => {
30+
jest.useFakeTimers();
31+
mockPushActivity.mockClear();
32+
mockPopActivity.mockClear();
33+
});
34+
2835
it('sets displayName properly', () => {
2936
const TestComponent = () => <h1>Hello World</h1>;
3037

3138
const ProfiledComponent = withProfiler(TestComponent);
3239
expect(ProfiledComponent.displayName).toBe('profiler(TestComponent)');
3340
});
3441

35-
describe('Tracing Integration', () => {
36-
beforeEach(() => {
37-
jest.useFakeTimers();
38-
mockPushActivity.mockClear();
39-
mockPopActivity.mockClear();
40-
});
42+
it('popActivity() is called when unmounted', () => {
43+
const ProfiledComponent = withProfiler(() => <h1>Hello World</h1>);
4144

42-
it('is called with popActivity() when unmounted', () => {
43-
const ProfiledComponent = withProfiler(() => <h1>Hello World</h1>);
45+
expect(mockPopActivity).toHaveBeenCalledTimes(0);
46+
const profiler = render(<ProfiledComponent />);
47+
profiler.unmount();
4448

45-
expect(mockPopActivity).toHaveBeenCalledTimes(0);
49+
jest.runAllTimers();
4650

47-
const profiler = render(<ProfiledComponent />);
48-
profiler.unmount();
51+
expect(mockPopActivity).toHaveBeenCalledTimes(1);
52+
expect(mockPopActivity).toHaveBeenLastCalledWith(1);
53+
});
4954

50-
jest.runAllTimers();
55+
it('pushActivity() is called when mounted', () => {
56+
const ProfiledComponent = withProfiler(() => <h1>Testing</h1>);
5157

52-
expect(mockPopActivity).toHaveBeenCalledTimes(1);
53-
expect(mockPopActivity).toHaveBeenLastCalledWith(1);
58+
expect(mockPushActivity).toHaveBeenCalledTimes(0);
59+
render(<ProfiledComponent />);
60+
expect(mockPushActivity).toHaveBeenCalledTimes(1);
61+
expect(mockPushActivity).toHaveBeenLastCalledWith(UNKNOWN_COMPONENT, {
62+
description: `<${UNKNOWN_COMPONENT}>`,
63+
op: 'react',
5464
});
65+
});
66+
});
67+
68+
describe('useProfiler()', () => {
69+
beforeEach(() => {
70+
jest.useFakeTimers();
71+
mockPushActivity.mockClear();
72+
mockPopActivity.mockClear();
73+
});
74+
75+
it('popActivity() is called when unmounted', () => {
76+
// tslint:disable-next-line: no-void-expression
77+
const profiler = renderHook(() => useProfiler('Example'));
78+
expect(mockPopActivity).toHaveBeenCalledTimes(0);
79+
profiler.unmount();
80+
81+
jest.runAllTimers();
82+
83+
expect(mockPopActivity).toHaveBeenCalled();
84+
expect(mockPopActivity).toHaveBeenLastCalledWith(1);
85+
});
5586

56-
describe('pushActivity()', () => {
57-
it('is called when mounted', () => {
58-
const ProfiledComponent = withProfiler(() => <h1>Testing</h1>);
59-
60-
expect(mockPushActivity).toHaveBeenCalledTimes(0);
61-
render(<ProfiledComponent />);
62-
expect(mockPushActivity).toHaveBeenCalledTimes(1);
63-
expect(mockPushActivity).toHaveBeenLastCalledWith(UNKNOWN_COMPONENT, {
64-
description: `<${UNKNOWN_COMPONENT}>`,
65-
op: 'react',
66-
});
67-
});
87+
it('pushActivity() is called when mounted', () => {
88+
expect(mockPushActivity).toHaveBeenCalledTimes(0);
89+
// tslint:disable-next-line: no-void-expression
90+
const profiler = renderHook(() => useProfiler('Example'));
91+
profiler.unmount();
92+
expect(mockPushActivity).toHaveBeenCalledTimes(1);
93+
expect(mockPushActivity).toHaveBeenLastCalledWith('Example', {
94+
description: `<Example>`,
95+
op: 'react',
6896
});
6997
});
7098
});

packages/react/tslint.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": ["@sentry/typescript/tslint", "tslint-react"],
2+
"extends": ["@sentry/typescript/tslint", "tslint-react", "tslint-react-hooks"],
33
"rules": {
44
"no-implicit-dependencies": [
55
true,

yarn.lock

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119
core-js-pure "^3.0.0"
120120
regenerator-runtime "^0.13.4"
121121

122-
"@babel/runtime@^7.10.2", "@babel/runtime@^7.7.4":
122+
"@babel/runtime@^7.10.2", "@babel/runtime@^7.5.4", "@babel/runtime@^7.7.4":
123123
version "7.10.2"
124124
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
125125
integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
@@ -1195,6 +1195,14 @@
11951195
dom-accessibility-api "^0.4.4"
11961196
pretty-format "^25.5.0"
11971197

1198+
"@testing-library/react-hooks@^3.3.0":
1199+
version "3.3.0"
1200+
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.3.0.tgz#dc217bfce8e7c34a99c811d73d23feef957b7c1d"
1201+
integrity sha512-rE9geI1+HJ6jqXkzzJ6abREbeud6bLF8OmF+Vyc7gBoPwZAEVBYjbC1up5nNoVfYBhO5HUwdD4u9mTehAUeiyw==
1202+
dependencies:
1203+
"@babel/runtime" "^7.5.4"
1204+
"@types/testing-library__react-hooks" "^3.0.0"
1205+
11981206
"@testing-library/react@^10.0.6":
11991207
version "10.0.6"
12001208
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.6.tgz#e1e569135badb7367cc6ac9823e376eccb0280e0"
@@ -1453,6 +1461,13 @@
14531461
dependencies:
14541462
"@types/react" "*"
14551463

1464+
"@types/react-test-renderer@*":
1465+
version "16.9.2"
1466+
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz#e1c408831e8183e5ad748fdece02214a7c2ab6c5"
1467+
integrity sha512-4eJr1JFLIAlWhzDkBCkhrOIWOvOxcCAfQh+jiKg7l/nNZcCIL2MHl2dZhogIFKyHzedVWHaVP1Yydq/Ruu4agw==
1468+
dependencies:
1469+
"@types/react" "*"
1470+
14561471
"@types/react@*", "@types/react@^16.9.35":
14571472
version "16.9.35"
14581473
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368"
@@ -1511,6 +1526,14 @@
15111526
dependencies:
15121527
pretty-format "^25.1.0"
15131528

1529+
"@types/testing-library__react-hooks@^3.0.0":
1530+
version "3.2.0"
1531+
resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.2.0.tgz#52f3a109bef06080e3b1e3ae7ea1c014ce859897"
1532+
integrity sha512-dE8iMTuR5lzB+MqnxlzORlXzXyCL0EKfzH0w/lau20OpkHD37EaWjZDz0iNG8b71iEtxT4XKGmSKAGVEqk46mw==
1533+
dependencies:
1534+
"@types/react" "*"
1535+
"@types/react-test-renderer" "*"
1536+
15141537
"@types/testing-library__react@^10.0.1":
15151538
version "10.0.1"
15161539
resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-10.0.1.tgz#92bb4a02394bf44428e35f1da2970ed77f803593"
@@ -1787,11 +1810,32 @@ after@0.8.2:
17871810
version "0.8.2"
17881811
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
17891812

1790-
agent-base@4, agent-base@5, agent-base@6, agent-base@^4.3.0, agent-base@~4.2.0:
1813+
agent-base@4, agent-base@^4.3.0:
1814+
version "4.3.0"
1815+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
1816+
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
1817+
dependencies:
1818+
es6-promisify "^5.0.0"
1819+
1820+
agent-base@5:
17911821
version "5.1.1"
17921822
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
17931823
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
17941824

1825+
agent-base@6:
1826+
version "6.0.0"
1827+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a"
1828+
integrity sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==
1829+
dependencies:
1830+
debug "4"
1831+
1832+
agent-base@~4.2.0:
1833+
version "4.2.1"
1834+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
1835+
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
1836+
dependencies:
1837+
es6-promisify "^5.0.0"
1838+
17951839
agentkeepalive@^3.4.1:
17961840
version "3.5.2"
17971841
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67"
@@ -4509,6 +4553,18 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0:
45094553
is-date-object "^1.0.1"
45104554
is-symbol "^1.0.2"
45114555

4556+
es6-promise@^4.0.3:
4557+
version "4.2.8"
4558+
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
4559+
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
4560+
4561+
es6-promisify@^5.0.0:
4562+
version "5.0.0"
4563+
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
4564+
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
4565+
dependencies:
4566+
es6-promise "^4.0.3"
4567+
45124568
escape-html@~1.0.3:
45134569
version "1.0.3"
45144570
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -9303,7 +9359,7 @@ react-dom@^16.0.0:
93039359
prop-types "^15.6.2"
93049360
scheduler "^0.19.1"
93059361

9306-
react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1:
9362+
react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6:
93079363
version "16.13.1"
93089364
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
93099365
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -9313,6 +9369,16 @@ react-is@^16.8.4:
93139369
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2"
93149370
integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==
93159371

9372+
react-test-renderer@^16.13.1:
9373+
version "16.13.1"
9374+
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1"
9375+
integrity sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==
9376+
dependencies:
9377+
object-assign "^4.1.1"
9378+
prop-types "^15.6.2"
9379+
react-is "^16.8.6"
9380+
scheduler "^0.19.1"
9381+
93169382
react@^16.0.0:
93179383
version "16.13.1"
93189384
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
@@ -11034,6 +11100,11 @@ tslint-consistent-codestyle@^1.15.1:
1103411100
tslib "^1.7.1"
1103511101
tsutils "^2.29.0"
1103611102

11103+
tslint-react-hooks@^2.2.2:
11104+
version "2.2.2"
11105+
resolved "https://registry.yarnpkg.com/tslint-react-hooks/-/tslint-react-hooks-2.2.2.tgz#4dc9b3986196802d45c11cc0bf6319a8116fe2ed"
11106+
integrity sha512-gtwA14+WevNUtlBhvAD5Ukpxt2qMegYI7IDD8zN/3JXLksdLdEuU/T/oqlI1CtZhMJffqyNn+aqq2oUqUFXiNA==
11107+
1103711108
tslint-react@^5.0.0:
1103811109
version "5.0.0"
1103911110
resolved "https://registry.yarnpkg.com/tslint-react/-/tslint-react-5.0.0.tgz#d0ae644e8163bdd3e134012e9353094904e8dd44"

0 commit comments

Comments
 (0)
0