diff --git a/src/index.ts b/src/index.ts
index 815250e..c7411d7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -29,10 +29,12 @@ export * from './lifecycle/components/TransitionPresence/TransitionPresence.cont
export * from './lifecycle/components/TransitionPresence/TransitionPresence.js';
export * from './lifecycle/hooks/useBeforeMount/useBeforeMount.js';
export * from './lifecycle/hooks/useBeforeUnmount/useBeforeUnmount.js';
+export * from './lifecycle/hooks/useIsFirstRender/useIsFirstRender.js';
export * from './lifecycle/hooks/useIsMounted/useIsMounted.js';
export * from './lifecycle/hooks/useIsMountedState/useIsMountedState.js';
export * from './lifecycle/hooks/useMount/useMount.js';
export * from './lifecycle/hooks/useUnmount/useUnmount.js';
+export * from './lifecycle/hooks/useUpdateEffect/useUpdateEffect.js';
export * from './types/PolymorphicComponentProps/PolymorphicComponentProps.js';
export * from './utils/adjustFontSize/adjustFontSize.js';
export * from './utils/arrayRef/arrayRef.js';
diff --git a/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.mdx b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.mdx
new file mode 100644
index 0000000..79f2963
--- /dev/null
+++ b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.mdx
@@ -0,0 +1,42 @@
+import { Meta } from '@storybook/blocks';
+
+
+
+# useIsFirstRender
+
+This hook is a useful for determining whether the current render is the first render of a component.
+Its is particularly handy when you want to conditionally execute certain logic or render specific
+components only on the initial render, providing an efficient way to differentiate between the first
+and subsequent renders.
+
+## Reference
+
+```ts
+function useIsFirstRender(): boolean;
+```
+
+### Returns
+
+- `true` on first render, `false` otherwise.
+
+## Usage
+
+```tsx
+function DemoComponent(): ReactElement {
+ const isFirstMount = useIsFirstRender();
+ const forceRerender = useForceRerender();
+
+ const onClick = useCallback(() => {
+ forceRerender();
+ }, [forceRerender]);
+
+ return (
+
+
{isFirstMount ? 'This is the first render.' : 'This is not the first render.'}
+
+ Rerender component
+
+
+ );
+}
+```
diff --git a/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.stories.tsx b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.stories.tsx
new file mode 100644
index 0000000..198be30
--- /dev/null
+++ b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.stories.tsx
@@ -0,0 +1,39 @@
+/* eslint-disable react/jsx-no-literals */
+import type { Meta, StoryObj } from '@storybook/react';
+import { useCallback, type ReactElement } from 'react';
+import { useForceRerender } from '../useForceRerender/useForceRerender.js';
+import { useIsFirstRender } from './useIsFirstRender.js';
+
+const meta = {
+ title: 'Hooks / useIsFirstRender',
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+function DemoComponent(): ReactElement {
+ const isFirstMount = useIsFirstRender();
+ const forceRerender = useForceRerender();
+
+ const onClick = useCallback((): void => {
+ forceRerender();
+ }, [forceRerender]);
+
+ return (
+
+
{isFirstMount ? 'This is the first render.' : 'This is not the first render.'}
+
+
+ Rerender component
+
+
+
+ );
+}
+
+export const Demo: Story = {
+ render() {
+ return ;
+ },
+};
diff --git a/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.test.tsx b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.test.tsx
new file mode 100644
index 0000000..9bc4c39
--- /dev/null
+++ b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.test.tsx
@@ -0,0 +1,20 @@
+import { act, renderHook } from '@testing-library/react';
+import { describe, it, expect } from 'vitest';
+import { useIsFirstRender } from './useIsFirstRender.js';
+
+describe('useIsFirstRender', () => {
+ it('should return true on first render and false on subsequent renders', async () => {
+ const { result, rerender } = renderHook(() => useIsFirstRender());
+
+ // Check if the hook returns true on the first render
+ expect(result.current).toBe(true);
+
+ // Force a rerender
+ await act(async () => {
+ rerender();
+ });
+
+ // Check if the hook returns false on subsequent renders
+ expect(result.current).toBe(false);
+ });
+});
diff --git a/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.ts b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.ts
new file mode 100644
index 0000000..55355d3
--- /dev/null
+++ b/src/lifecycle/hooks/useIsFirstRender/useIsFirstRender.ts
@@ -0,0 +1,16 @@
+import { useRef } from 'react';
+
+/**
+ * A hook that returns a boolean that is `true` only on first render.
+ */
+export function useIsFirstRender(): boolean {
+ const isFirst = useRef(true);
+
+ if (isFirst.current) {
+ isFirst.current = false;
+
+ return true;
+ }
+
+ return isFirst.current;
+}
diff --git a/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.mdx b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.mdx
new file mode 100644
index 0000000..4dcabeb
--- /dev/null
+++ b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.mdx
@@ -0,0 +1,53 @@
+import { Meta } from '@storybook/blocks';
+
+
+
+# useUpdateEffect
+
+A modified version of `useEffect` that is skipping the first render (mount).
+
+## Reference
+
+```ts
+function useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void;
+```
+
+### Parameters
+
+- `effect` – Function to run on updates.
+- `deps` – Dependencies list, as for `useEffect` hook
+
+### Returns
+
+- void
+
+## Usage
+
+```tsx
+function DemoComponent(): ReactElement {
+ const [date, setDate] = useState();
+
+ useEffect(() => {
+ // eslint-disable-next-line no-console
+ console.log('Normal useEffect', date);
+ }, [date]);
+
+ useUpdateEffect(() => {
+ // eslint-disable-next-line no-console
+ console.log('Update useEffect only', date);
+ }, [date]);
+
+ const onClick = useCallback(() => {
+ setDate(Date.now());
+ }, []);
+
+ return (
+
+
Open your console
+
+ Update
+
+
+ );
+}
+```
diff --git a/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.stories.tsx b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.stories.tsx
new file mode 100644
index 0000000..2785166
--- /dev/null
+++ b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.stories.tsx
@@ -0,0 +1,52 @@
+/* eslint-disable react/jsx-no-literals */
+import type { Meta, StoryObj } from '@storybook/react';
+import { useState, type ReactElement, useCallback, useEffect } from 'react';
+import { useUpdateEffect } from './useUpdateEffect.js';
+
+const meta = {
+ title: 'Hooks / useUpdateEffect',
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+function DemoComponent(): ReactElement {
+ const [date, setDate] = useState();
+
+ useEffect(() => {
+ // eslint-disable-next-line no-console
+ console.log('Normal useEffect', date);
+ }, [date]);
+
+ useUpdateEffect(() => {
+ // eslint-disable-next-line no-console
+ console.log('Update useUpdateEffect only', date);
+ }, [date]);
+
+ const onClick = useCallback(() => {
+ setDate(Date.now());
+ }, []);
+
+ return (
+
+
+
Open your console
+
+ Value: {date ? 'true' : 'false'}
+
+
+
+
+ Update
+
+
+
+ );
+}
+
+export const Demo: Story = {
+ render() {
+ return ;
+ },
+};
diff --git a/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.test.tsx b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.test.tsx
new file mode 100644
index 0000000..dd562a1
--- /dev/null
+++ b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.test.tsx
@@ -0,0 +1,18 @@
+import { renderHook } from '@testing-library/react';
+import { describe, it, vitest, expect } from 'vitest';
+import { useUpdateEffect } from './useUpdateEffect.js';
+
+describe('useUpdateEffect', () => {
+ it('callback function should have been called on update', () => {
+ const effect = vitest.fn();
+ const { rerender } = renderHook(() => {
+ useUpdateEffect(effect);
+ });
+
+ expect(effect).not.toHaveBeenCalled();
+
+ rerender();
+
+ expect(effect).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.ts b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.ts
new file mode 100644
index 0000000..72cd5f9
--- /dev/null
+++ b/src/lifecycle/hooks/useUpdateEffect/useUpdateEffect.ts
@@ -0,0 +1,13 @@
+import { type DependencyList, type EffectCallback, useEffect } from 'react';
+import { useIsFirstRender } from '../useIsFirstRender/useIsFirstRender.js';
+
+/**
+ * This hook ignores the first render, so it's not invoked on mount.
+ *
+ * @param effect Function to run on updates
+ * @param deps Dependencies list, as for `useEffect` hook
+ */
+export function useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void {
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ useEffect(useIsFirstRender() ? (): undefined => undefined : effect, deps);
+}