Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const menus = [
'useUpdateEffect',
'useUpdateLayoutEffect',
'useAsyncEffect',
'useImmediateEffect',
'useDebounceEffect',
'useDebounceFn',
'useThrottleFn',
Expand Down
2 changes: 2 additions & 0 deletions packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import useWebSocket from './useWebSocket';
import useWhyDidYouUpdate from './useWhyDidYouUpdate';
import useMutationObserver from './useMutationObserver';
import useTheme from './useTheme';
import useImmediateEffect from './useImmediateEffect';

export {
useRequest,
Expand Down Expand Up @@ -158,4 +159,5 @@ export {
useResetState,
useMutationObserver,
useTheme,
useImmediateEffect,
};
33 changes: 33 additions & 0 deletions packages/hooks/src/useImmediateEffect/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { renderHook } from '@testing-library/react';
import useImmediateEffect from '../index';
import { describe, expect, it } from 'vitest';

describe('useImmediateEffect', () => {
it('test immediate', async () => {
let mountedState = 1;
const hook = renderHook(() => {
useImmediateEffect(() => {
mountedState = 2;
});
mountedState = 3;
});
expect(mountedState).toBe(3);
hook.rerender();
expect(mountedState).toBe(3);
});
it('test on optional', () => {
let mountedState = 1;
let effectCount = 0;
const hook = renderHook(() => {
useImmediateEffect(() => {
++effectCount;
}, [mountedState]);
});
expect(effectCount).toBe(1);
hook.rerender();
expect(effectCount).toBe(1);
mountedState = 2;
hook.rerender();
expect(mountedState).toBe(2);
});
});
48 changes: 48 additions & 0 deletions packages/hooks/src/useImmediateEffect/demo/demo1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* title: Basic usage
* desc: This hook is exactly the same as useEffect, except it calls effect immediately.
*
* title.zh-CN: 基础用法
* desc.zh-CN: 使用上与 useEffect 完全相同,但它会立刻执行副作用函数。
*/

import React, { useEffect, useRef, useState } from 'react';
import { useImmediateEffect } from 'ahooks';

export default () => {
const [count, setCount] = useState(0);
const shouldAlert = useRef(false);

if (count > 0 && shouldAlert.current) {
alert('FC start');
}

useImmediateEffect(() => {
if (count > 0) {
alert('immediate effect');
}
}, [count]);

useEffect(() => {
if (count > 0) {
alert('effect');
}
}, [count]);

if (count > 0 && shouldAlert.current) {
alert('FC end');
shouldAlert.current = false;
}

return (
<button
type="button"
onClick={() => {
setCount(count + 1);
shouldAlert.current = true
}}
>
reRender
</button>
);
};
25 changes: 25 additions & 0 deletions packages/hooks/src/useImmediateEffect/index.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
nav:
path: /hooks
---

# useImmediateEffect

A hook alike `useEffect` but calls the effect immediately instead of after render when dependencies changed.

## Examples

### Basic usage

<code src="./demo/demo1.tsx" />

## API

The API is exactly the same as `React.useEffect`.

```typescript
useImmediateEffect(
effect: React.EffectCallback,
deps?: React.DependencyList,
)
```
35 changes: 35 additions & 0 deletions packages/hooks/src/useImmediateEffect/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useRef, type EffectCallback, type DependencyList } from "react";

type Cleanup = ReturnType<EffectCallback>;

const useImmediateEffect = (effect: EffectCallback, deps?: DependencyList) => {
const prevDepsRef = useRef<DependencyList | undefined>(undefined);
const prevCleanupRef = useRef<Cleanup | undefined>(undefined);
if (isDepsChanged(prevDepsRef.current, deps)) {
prevCleanupRef.current?.();
prevCleanupRef.current = effect();
}
prevDepsRef.current = deps;
};

export default useImmediateEffect;

const isDepsChanged = (
prevDeps: DependencyList | undefined,
deps: DependencyList | undefined
) => {
if (
prevDeps instanceof Array &&
deps instanceof Array &&
prevDeps.length === deps.length
) {
for (let i = 0; i !== prevDeps.length; ++i) {
if (!Object.is(prevDeps[i], deps[i])) {
return true;
}
}
return false;
} else {
return true;
}
};
25 changes: 25 additions & 0 deletions packages/hooks/src/useImmediateEffect/index.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
nav:
path: /hooks
---

# useImmediateEffect

`useImmediateEffect` 用法类似于 `useEffect`,但是在依赖项变更后,会立刻执行副作用函数,而非在渲染结束后。

## 代码演示

### 基础用法

<code src="./demo/demo1.tsx" />

## API

API 与 `React.useEffect` 完全一致。

```typescript
useImmediateEffect(
effect: React.EffectCallback,
deps?: React.DependencyList,
)
```