Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: streamich/react-use
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v12.5.0
Choose a base ref
...
head repository: streamich/react-use
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v12.6.0
Choose a head ref
  • 13 commits
  • 16 files changed
  • 5 contributors

Commits on Sep 15, 2019

  1. Copy the full SHA
    66f206e View commit details

Commits on Oct 13, 2019

  1. Copy the full SHA
    5bf8f8e View commit details

Commits on Oct 14, 2019

  1. Copy the full SHA
    7dd7ba8 View commit details
  2. Copy the full SHA
    d315b6c View commit details
  3. Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    317c81e View commit details
  4. Merge pull request #607 from tanem/tslib

    Use tslib to dedupe TypeScript helper functions from built files
    xobotyi authored Oct 14, 2019
    Copy the full SHA
    88bbc47 View commit details
  5. Copy the full SHA
    828e116 View commit details
  6. Copy the full SHA
    100a340 View commit details

Commits on Oct 15, 2019

  1. Update Node.js to v12.12.0

    renovate-bot authored and renovate[bot] committed Oct 15, 2019
    1
    Copy the full SHA
    695faa5 View commit details
  2. Copy the full SHA
    9e4ad43 View commit details

Commits on Oct 16, 2019

  1. docs: code coverage (#682)

    wardoost authored Oct 16, 2019
    Copy the full SHA
    1fa1045 View commit details
  2. feat: useRafState (#684)

    wardoost authored Oct 16, 2019
    Copy the full SHA
    00816a4 View commit details
  3. chore(release): 12.6.0 [skip ci]

    # [12.6.0](v12.5.0...v12.6.0) (2019-10-16)
    
    ### Features
    
    * useRafState ([#684](#684)) ([00816a4](00816a4))
    semantic-release-bot committed Oct 16, 2019
    Copy the full SHA
    109c6c4 View commit details
Showing with 306 additions and 79 deletions.
  1. +1 βˆ’1 .circleci/config.yml
  2. +7 βˆ’0 CHANGELOG.md
  3. +62 βˆ’0 CONTRIBUTING.md
  4. +1 βˆ’0 README.md
  5. +33 βˆ’0 docs/useRafState.md
  6. +6 βˆ’4 package.json
  7. +31 βˆ’0 src/__stories__/useRafState.story.tsx
  8. +83 βˆ’0 src/__tests__/useRafState.test.ts
  9. +2 βˆ’0 src/index.ts
  10. +21 βˆ’25 src/useMouse.ts
  11. +24 βˆ’0 src/useRafState.ts
  12. +10 βˆ’17 src/useScroll.ts
  13. +7 βˆ’10 src/useWindowScroll.ts
  14. +7 βˆ’12 src/useWindowSize.ts
  15. +2 βˆ’1 tsconfig.json
  16. +9 βˆ’9 yarn.lock
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ version: 2
refs:
container: &container
docker:
- image: node:12.9.1
- image: node:12.12.0
working_directory: ~/repo
steps:
- &Versions
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# [12.6.0](https://github.com/streamich/react-use/compare/v12.5.0...v12.6.0) (2019-10-16)


### Features

* useRafState ([#684](https://github.com/streamich/react-use/issues/684)) ([00816a4](https://github.com/streamich/react-use/commit/00816a4))

# [12.5.0](https://github.com/streamich/react-use/compare/v12.4.0...v12.5.0) (2019-10-13)


62 changes: 62 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Contributing

Thanks for being willing to contribute πŸ™Œ If you contribute to this project, you agree to release your work under the license of this project.

**Working on your first Pull Request?** You can learn how from this [First Contributions](https://github.com/firstcontributions/first-contributions) guide.

## Project setup

1. Fork and clone the repo
1. Run `yarn install` to install dependencies
1. Create a branch for your PR with `git checkout -b pr/your-branch-name`

> Tip: Keep your `master` branch pointing at the original repository and make
> pull requests from branches on your fork. To do this, run:
>
> ```sh
> git remote add upstream https://github.com/streamich/react-use.git
> git fetch upstream
> git branch --set-upstream-to=upstream/master master
> ```
>
> This will add the original repository as a "remote" called "upstream," Then
> fetch the git information from that remote, then set your local `master`
> branch to use the upstream master branch whenever you run `git pull`. Then you
> can make all of your pull request branches based on this `master` branch.
> Whenever you want to update your version of `master`, do a regular `git pull`.
## Development
This library is a collection of React hooks so a proposal for a new hook will need to utilize the [React Hooks API](https://reactjs.org/docs/hooks-reference.html) internally to be taken into consideration.
### Creating a new hook
1. Create `src/useYourHookName.ts` and `src/__stories__/useYourHookName.story.tsx`, run `yarn start` to start the storybook development server and start coding your hook
1. Create `src/__tests__/useYourHookName.test.ts`, run `yarn test:watch` to start the test runner in watch mode and start writing tests for your hook
1. Create `src/docs/useYourHookName.md` and create documentation for your hook
1. Export your hook from `src/index.ts` and add your hook to `README.md`
You can also write your tests first if you prefer [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development).
### Updating an existing hook
1. Run `yarn start` to start the storybook development server and start applying changes
2. Update tests according to your changes using `yarn test:watch`
3. Update documentation according to your changes
## Committing and Pushing changes
### Commit messages
This repo uses [semantic-release](https://github.com/semantic-release/semantic-release) and [conventional commit messages](https://conventionalcommits.org) so prefix your commits with `fix:` or `feat:` if you want your changes to appear in [release notes](https://github.com/streamich/react-use/blob/master/CHANGELOG.md).
### Git hooks
There are git hooks set up with this project that are automatically enabled
when you install dependencies. These hooks automatically test and validate your code when creating commits. They're really handy but can be temporarily disabled by adding a `--no-verify` flag to your commit command. This is useful when you want to commit and push to get feedback on uncompleted code.
## Help needed
Please have a look at the [open issues](https://github.com/streamich/react-use/issues) and respond to questions, bug reports and feature requests. Thanks!
We're also looking to improve the code coverage on this project. To easily know what hooks need tests run `yarn test:coverage` to generate a code coverage report. You can see the report in your terminal or open `coverage/lcov-report/index.html` to see the HTML report.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -128,6 +128,7 @@
- [`useGetSetState`](./docs/useGetSetState.md) — as if [`useGetSet`](./docs/useGetSet.md) and [`useSetState`](./docs/useSetState.md) had a baby.
- [`usePrevious`](./docs/usePrevious.md) — returns the previous state or props. [![][img-demo]](https://codesandbox.io/s/fervent-galileo-krgx6)
- [`useObservable`](./docs/useObservable.md) — tracks latest value of an `Observable`.
- [`useRafState`](./docs/useRafState.md) — creates `setState` method which only updates after `requestAnimationFrame`. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-userafstate--demo)
- [`useSetState`](./docs/useSetState.md) — creates `setState` method which works like `this.setState`. [![][img-demo]](https://codesandbox.io/s/n75zqn1xp0)
- [`useStateList`](./docs/useStateList.md) — circularly iterates over an array. [![][img-demo]](https://codesandbox.io/s/bold-dewdney-pjzkd)
- [`useToggle` and `useBoolean`](./docs/useToggle.md) — tracks state of a boolean. [![][img-demo]](https://codesandbox.io/s/focused-sammet-brw2d)
33 changes: 33 additions & 0 deletions docs/useRafState.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# `useRafState`

React state hook that only updates state in the callback of [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).

## Usage

```jsx
import {useRafState, useMount} from 'react-use';

const Demo = () => {
const [state, setState] = useRafState({
width: 0,
height: 0,
});

useMount(() => {
const onResize = () => {
setState({
width: window.clientWidth,
height: window.height,
});
};

window.addEventListener('resize', onResize);

return () => {
window.removeEventListener('resize', onResize);
};
});

return <pre>{JSON.stringify(state, null, 2)}</pre>;
};
```
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-use",
"version": "12.5.0",
"version": "12.6.0",
"description": "Collection of React Hooks",
"main": "lib/index.js",
"module": "esm/index.js",
@@ -15,6 +15,7 @@
"start": "yarn storybook",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "tslint 'src/**/*.{ts,tsx}' -t verbose",
"lint:fix": "yarn lint --fix",
"lint:types": "tsc --noEmit",
@@ -54,7 +55,8 @@
"screenfull": "^5.0.0",
"set-harmonic-interval": "^1.0.0",
"throttle-debounce": "^2.0.1",
"ts-easing": "^0.2.0"
"ts-easing": "^0.2.0",
"tslib": "^1.10.0"
},
"peerDependencies": {
"react": "^16.8.0",
@@ -76,12 +78,12 @@
"@storybook/addon-options": "5.1.11",
"@storybook/react": "5.1.11",
"@testing-library/react-hooks": "2.0.3",
"@types/jest": "24.0.18",
"@types/jest": "24.0.19",
"@types/react": "16.9.2",
"babel-core": "6.26.3",
"babel-loader": "8.0.6",
"babel-plugin-dynamic-import-node": "2.3.0",
"fork-ts-checker-webpack-plugin": "1.5.0",
"fork-ts-checker-webpack-plugin": "1.5.1",
"gh-pages": "2.1.1",
"husky": "3.0.9",
"jest": "24.9.0",
31 changes: 31 additions & 0 deletions src/__stories__/useRafState.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import { useRafState, useMount } from '..';
import ShowDocs from './util/ShowDocs';

const Demo = () => {
const [state, setState] = useRafState({ x: 0, y: 0 });

useMount(() => {
const onMouseMove = (event: MouseEvent) => {
setState({ x: event.clientX, y: event.clientY });
};
const onTouchMove = (event: TouchEvent) => {
setState({ x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY });
};

document.addEventListener('mousemove', onMouseMove);
document.addEventListener('touchmove', onTouchMove);

return () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('touchmove', onTouchMove);
};
});

return <pre>{JSON.stringify(state, null, 2)}</pre>;
};

storiesOf('State|useRafState', module)
.add('Docs', () => <ShowDocs md={require('../../docs/useRafState.md')} />)
.add('Demo', () => <Demo />);
83 changes: 83 additions & 0 deletions src/__tests__/useRafState.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { act, renderHook } from '@testing-library/react-hooks';
import { replaceRaf } from 'raf-stub';
import useRafState from '../useRafState';

interface RequestAnimationFrame {
reset(): void;
step(): void;
}

declare var requestAnimationFrame: RequestAnimationFrame;

replaceRaf();

beforeEach(() => {
requestAnimationFrame.reset();
});

afterEach(() => {
requestAnimationFrame.reset();
});

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

it('should only update state after requestAnimationFrame when providing an object', () => {
const { result } = renderHook(() => useRafState(0));

act(() => {
result.current[1](1);
});
expect(result.current[0]).toBe(0);

act(() => {
requestAnimationFrame.step();
});
expect(result.current[0]).toBe(1);

act(() => {
result.current[1](2);
requestAnimationFrame.step();
});
expect(result.current[0]).toBe(2);

act(() => {
result.current[1](prevState => prevState * 2);
requestAnimationFrame.step();
});
expect(result.current[0]).toBe(4);
});

it('should only update state after requestAnimationFrame when providing a function', () => {
const { result } = renderHook(() => useRafState(0));

act(() => {
result.current[1](prevState => prevState + 1);
});
expect(result.current[0]).toBe(0);

act(() => {
requestAnimationFrame.step();
});
expect(result.current[0]).toBe(1);

act(() => {
result.current[1](prevState => prevState * 3);
requestAnimationFrame.step();
});
expect(result.current[0]).toBe(3);
});

it('should cancel update state on unmount', () => {
const { unmount } = renderHook(() => useRafState(0));
const spyRafCancel = jest.spyOn(global, 'cancelAnimationFrame' as any);

expect(spyRafCancel).not.toHaveBeenCalled();

unmount();

expect(spyRafCancel).toHaveBeenCalledTimes(1);
});
});
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -60,6 +60,8 @@ export { default as usePreviousDistinct } from './usePreviousDistinct';
export { default as usePromise } from './usePromise';
export { default as useRaf } from './useRaf';
export { default as useRafLoop } from './useRafLoop';
export { default as useRafState } from './useRafState';

/**
* @deprecated This hook is obsolete, use `useMountedState` instead
*/
46 changes: 21 additions & 25 deletions src/useMouse.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { RefObject, useEffect, useRef, useState } from 'react';
import { RefObject, useEffect } from 'react';

import useRafState from './useRafState';

export interface State {
docX: number;
@@ -18,8 +20,7 @@ const useMouse = (ref: RefObject<Element>): State => {
}
}

const frame = useRef(0);
const [state, setState] = useState<State>({
const [state, setState] = useRafState<State>({
docX: 0,
docY: 0,
posX: 0,
@@ -32,34 +33,29 @@ const useMouse = (ref: RefObject<Element>): State => {

useEffect(() => {
const moveHandler = (event: MouseEvent) => {
cancelAnimationFrame(frame.current);

frame.current = requestAnimationFrame(() => {
if (ref && ref.current) {
const { left, top, width: elW, height: elH } = ref.current.getBoundingClientRect();
const posX = left + window.pageXOffset;
const posY = top + window.pageYOffset;
const elX = event.pageX - posX;
const elY = event.pageY - posY;
if (ref && ref.current) {
const { left, top, width: elW, height: elH } = ref.current.getBoundingClientRect();
const posX = left + window.pageXOffset;
const posY = top + window.pageYOffset;
const elX = event.pageX - posX;
const elY = event.pageY - posY;

setState({
docX: event.pageX,
docY: event.pageY,
posX,
posY,
elX,
elY,
elH,
elW,
});
}
});
setState({
docX: event.pageX,
docY: event.pageY,
posX,
posY,
elX,
elY,
elH,
elW,
});
}
};

document.addEventListener('mousemove', moveHandler);

return () => {
cancelAnimationFrame(frame.current);
document.removeEventListener('mousemove', moveHandler);
};
}, [ref]);
24 changes: 24 additions & 0 deletions src/useRafState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useRef, useState, useCallback, Dispatch, SetStateAction } from 'react';

import useUnmount from './useUnmount';

const useRafState = <S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>] => {
const frame = useRef(0);
const [state, setState] = useState(initialState);

const setRafState = useCallback((value: S | ((prevState: S) => S)) => {
cancelAnimationFrame(frame.current);

frame.current = requestAnimationFrame(() => {
setState(value);
});
}, []);

useUnmount(() => {
cancelAnimationFrame(frame.current);
});

return [state, setRafState];
};

export default useRafState;
Loading