diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md
index 4a137613..0d1b5306 100644
--- a/docs/README.zh-CN.md
+++ b/docs/README.zh-CN.md
@@ -96,6 +96,7 @@ $ yarn add beautiful-react-hooks
* [useSessionStorage](./useSessionStorage.md)
* [useStorage](./useStorage.md)
* [useDefaultedState](./useDefaultedState.md)
+* [useRenderInfo](docs/useRenderInfo.md)
diff --git a/docs/useRenderInfo.md b/docs/useRenderInfo.md
new file mode 100644
index 00000000..d3dee6a3
--- /dev/null
+++ b/docs/useRenderInfo.md
@@ -0,0 +1,72 @@
+# useRenderInfo
+
+Takes a component name and prints information on how many time the component renders, at what time and how many seconds
+has passed since the last render.
+
+### Why? 💡
+
+- Easily display information on components render
+
+### Basic Usage:
+
+```jsx harmony
+import useInterval from 'beautiful-react-hooks/useInterval';
+import useRenderInfo from 'beautiful-react-hooks/useRenderInfo';
+
+
+const RenderInfo = () => {
+ const [seconds, setSeconds] = React.useState(0);
+
+ // repeat the function each 1000ms
+ useInterval(() => {
+ setSeconds(1 + seconds);
+ }, 1000);
+
+
+ useRenderInfo('Module');
+
+ return (
+
+ Check the console!
+
+ );
+};
+
+
+```
+
+### Custom logs:
+
+```jsx harmony
+import useInterval from 'beautiful-react-hooks/useInterval';
+import useRenderInfo from 'beautiful-react-hooks/useRenderInfo';
+
+
+const RenderInfo = () => {
+ const [seconds, setSeconds] = React.useState(0);
+ const info = useRenderInfo();
+
+ // repeat the function each 1000ms
+ useInterval(() => {
+ setSeconds(1 + seconds);
+ }, 1000);
+
+ return (
+
+ I'm not using the console, {info.sinceLast} seconds passed from the last render!
+
+ );
+};
+
+
+```
+
+### Mastering the hook
+
+#### ✅ When to use
+
+- When debugging a component
+
+#### 🛑 What not to do
+
+- In production build, you don't want useless logs in console :)
diff --git a/index.d.ts b/index.d.ts
index e21b7318..388fecfd 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -16,8 +16,8 @@ type EventListenerOptions = {
passive: boolean,
}
-type HandlerSetter > = (a: T) => void;
-
+type HandlerSetter> = (a: T) => void;
+
type Cancelable = {
cancel(): void;
flush(): void;
@@ -268,3 +268,13 @@ type SpeechOptions = {
}
export declare const useSpeechSynthesis: (text: string, options?: SpeechOptions) => ({ speak: Function, speechSynthUtterance: SpeechSynthesisUtterance });
+
+
+type RenderInfo = {
+ module: string,
+ renders: number,
+ timestamp: number,
+ sinceLast: string,
+}
+
+export declare const useRenderInfo: (name?: string, log?: boolean) => RenderInfo;
diff --git a/package.json b/package.json
index 0f19ea3d..dd6847b8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "beautiful-react-hooks",
- "version": "0.30.6",
+ "version": "0.31.0",
"description": "A collection of beautiful (and hopefully useful) React hooks to speed-up your components and hooks development",
"main": "index.js",
"module": "esm/index.js",
diff --git a/src/index.js b/src/index.js
index 0f73c491..6ce0455a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -33,3 +33,4 @@ export { default as useDefaultedState } from './useDefaultedState';
export { default as useObservable } from './useObservable';
export { default as useSpeechSynthesis } from './useSpeechSynthesis';
export { default as useSystemVoices } from './useSystemVoices';
+export { default as useRenderInfo } from './useRenderInfo';
diff --git a/src/useRenderInfo.js b/src/useRenderInfo.js
new file mode 100644
index 00000000..895dd66c
--- /dev/null
+++ b/src/useRenderInfo.js
@@ -0,0 +1,36 @@
+import { useRef } from 'react';
+
+const getInitial = (module) => ({
+ module,
+ renders: 0,
+ timestamp: null,
+ sinceLast: null,
+});
+
+/**
+ * useRenderInfo
+ * @param module
+ * @param log
+ * @returns {{renders: number, module: *, timestamp: null}}
+ */
+const useRenderInfo = (module = 'Unknown component', log = true) => {
+ const { current: info } = useRef(getInitial(module));
+ const now = +Date.now();
+
+ info.renders += 1;
+ info.sinceLast = info.timestamp ? (now - info.timestamp) / 1000 : '[now]';
+ info.timestamp = now;
+
+ if (log) {
+ /* eslint-disable no-console */
+ console.group(`${module} info`);
+ console.log(`Render no: ${info.renders}${info.renders > 1 ? `, ${info.sinceLast}s since last render` : ''}`);
+ console.dir(info);
+ console.groupEnd();
+ /* eslint-enable no-console */
+ }
+
+ return info;
+};
+
+export default useRenderInfo;
diff --git a/test/useRenderInfo.spec.js b/test/useRenderInfo.spec.js
new file mode 100644
index 00000000..d8d03cc5
--- /dev/null
+++ b/test/useRenderInfo.spec.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import { cleanup, renderHook } from '@testing-library/react-hooks';
+import useRenderInfo from '../dist/useRenderInfo';
+
+describe('useRenderInfo', () => {
+ beforeEach(cleanup);
+
+ afterEach(sinon.restore);
+
+ it('should be a function', () => {
+ expect(useRenderInfo).to.be.a('function');
+ });
+
+ it('should return an information object', () => {
+ const name = 'Foo';
+ const { result: { current: info } } = renderHook(() => useRenderInfo(name, false));
+
+ expect(info).to.be.an('object');
+ expect(info.module).to.equal(name);
+ expect(info.renders).to.be.a('number');
+ expect(info.sinceLast).to.be.a('string');
+ expect(info.timestamp).to.be.a('number');
+ });
+
+ it('should print consistent information', () => {
+ const { result: { current: info }, rerender } = renderHook(() => useRenderInfo('foo', false));
+
+ rerender();
+ rerender();
+
+ expect(info.renders).to.equal(3);
+ });
+
+ it('should print renders information in group', () => {
+ const groupSpy = sinon.spy(console, 'group');
+ const groupEndSpy = sinon.spy(console, 'groupEnd');
+ const logSpy = sinon.spy(console, 'log');
+
+ renderHook(() => useRenderInfo(name, true));
+
+ expect(logSpy.called).to.be.true;
+ expect(groupSpy.called).to.be.true;
+ expect(groupEndSpy.called).to.be.true;
+ });
+});