Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add useFirstMountState & useRendersCount hooks #769

Merged
merged 3 commits into from
Nov 23, 2019
Merged
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@
- [`useStateValidator`](./docs/useStateValidator.md) — tracks state of an object. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usestatevalidator--demo)
- [`useMultiStateValidator`](./docs/useMultiStateValidator.md) — alike the `useStateValidator`, but tracks multiple states at a time. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usemultistatevalidator--demo)
- [`useMediatedState`](./docs/useMediatedState.md) — like the regular `useState` but with mediation by custom function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usemediatedstate--demo)
- [`useFirstMountState`](./docs/useFirstMountState.md) — check if current render is first. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usefirstmountstate--demo)
- [`useRendersCount`](./docs/useRendersCount.md) — count component renders. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-userenderscount--demo)
<br/>
<br/>
- [**Miscellaneous**]()
Expand Down
29 changes: 29 additions & 0 deletions docs/useFirstMountState.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# `useFirstMountState`

Returns `true` if component is just mounted (on first render) and `false` otherwise.

## Usage

```typescript jsx
import * as React from 'react';
import { useFirstMountState } from 'react-use';

const Demo = () => {
const isFirstMount = useFirstMountState();
const update = useUpdate();

return (
<div>
<span>This component is just mounted: {isFirstMount ? 'YES' : 'NO'}</span>
<br />
<button onClick={update}>re-render</button>
</div>
);
};
```

## Reference

```typescript
const isFirstMount: boolean = useFirstMountState();
```
29 changes: 29 additions & 0 deletions docs/useRendersCount.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# `useRendersCount`

Tracks compontent's renders count including the first render.

## Usage

```typescript jsx
import * as React from 'react';
import { useRendersCount } from "react-use";

const Demo = () => {
const update = useUpdate();
const rendersCount = useRendersCount();

return (
<div>
<span>Renders count: {rendersCount}</span>
<br />
<button onClick={update}>re-render</button>
</div>
);
};
```

## Reference

```typescript
const rendersCount: number = useRendersCount();
```
22 changes: 22 additions & 0 deletions src/__stories__/useFirstMountState.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import { useFirstMountState } from '../useFirstMountState';
import useUpdate from '../useUpdate';
import ShowDocs from './util/ShowDocs';

const Demo = () => {
const isFirstMount = useFirstMountState();
const update = useUpdate();

return (
<div>
<span>This component is just mounted: {isFirstMount ? 'YES' : 'NO'}</span>
<br />
<button onClick={update}>re-render</button>
</div>
);
};

storiesOf('State|useFirstMountState', module)
.add('Docs', () => <ShowDocs md={require('../../docs/useFirstMountState.md')} />)
.add('Demo', () => <Demo />);
22 changes: 22 additions & 0 deletions src/__stories__/useRendersCount.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import { useRendersCount } from '../useRendersCount';
import useUpdate from '../useUpdate';
import ShowDocs from './util/ShowDocs';

const Demo = () => {
const update = useUpdate();
const rendersCount = useRendersCount();

return (
<div>
<span>Renders count: {rendersCount}</span>
<br />
<button onClick={update}>re-render</button>
</div>
);
};

storiesOf('State|useRendersCount', module)
.add('Docs', () => <ShowDocs md={require('../../docs/useRendersCount.md')} />)
.add('Demo', () => <Demo />);
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,6 @@ export { useMultiStateValidator } from './useMultiStateValidator';
export { default as useWindowScroll } from './useWindowScroll';
export { default as useWindowSize } from './useWindowSize';
export { default as useMeasure } from './useMeasure';
export { useRendersCount } from './useRendersCount';
export { useFirstMountState } from './useFirstMountState';
export { default as useSet } from './useSet';
13 changes: 13 additions & 0 deletions src/useFirstMountState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useRef } from 'react';

export function useFirstMountState(): boolean {
const isFirst = useRef(true);

if (isFirst.current) {
isFirst.current = false;

return true;
}

return isFirst.current;
}
7 changes: 3 additions & 4 deletions src/usePreviousDistinct.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useRef } from 'react';
import { useFirstMountState } from './useFirstMountState';

export type Predicate<T> = (prev: T | undefined, next: T) => boolean;

Expand All @@ -7,11 +8,9 @@ const strictEquals = <T>(prev: T | undefined, next: T) => prev === next;
export default function usePreviousDistinct<T>(value: T, compare: Predicate<T> = strictEquals): T | undefined {
const prevRef = useRef<T>();
const curRef = useRef<T>(value);
const firstRender = useRef(true);
const isFirstMount = useFirstMountState();

if (firstRender.current) {
firstRender.current = false;
} else if (!compare(curRef.current, value)) {
if (!isFirstMount && !compare(curRef.current, value)) {
prevRef.current = curRef.current;
curRef.current = value;
}
Expand Down
5 changes: 5 additions & 0 deletions src/useRendersCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useRef } from 'react';

export function useRendersCount(): number {
return ++useRef(0).current;
}
11 changes: 4 additions & 7 deletions src/useUpdateEffect.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { useEffect, useRef } from 'react';
import { useEffect } from 'react';
import { useFirstMountState } from './useFirstMountState';

const useUpdateEffect: typeof useEffect = (effect, deps) => {
const isInitialMount = useRef(true);
const isFirstMount = useFirstMountState();

useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
return effect();
}
!isFirstMount && effect();
}, deps);
};

Expand Down
22 changes: 22 additions & 0 deletions tests/useFirstMountState.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { renderHook } from '@testing-library/react-hooks';
import { useFirstMountState } from '../src';

describe('useFirstMountState', () => {
it('should be defined', () => {
expect(useFirstMountState).toBeDefined();
});

it('should return boolean', () => {
expect(renderHook(() => useFirstMountState()).result.current).toEqual(expect.any(Boolean));
});

it('should return true on first render and false on all others', () => {
const hook = renderHook(() => useFirstMountState());

expect(hook.result.current).toBe(true);
hook.rerender();
expect(hook.result.current).toBe(false);
hook.rerender();
expect(hook.result.current).toBe(false);
});
});
22 changes: 22 additions & 0 deletions tests/useRendersCount.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { renderHook } from '@testing-library/react-hooks';
import { useRendersCount } from '../src';

describe('useRendersCount', () => {
it('should be defined', () => {
expect(useRendersCount).toBeDefined();
});

it('should return number', () => {
expect(renderHook(() => useRendersCount()).result.current).toEqual(expect.any(Number));
});

it('should return actual number of renders', () => {
const hook = renderHook(() => useRendersCount());

expect(hook.result.current).toBe(1);
hook.rerender();
expect(hook.result.current).toBe(2);
hook.rerender();
expect(hook.result.current).toBe(3);
});
});