GithubHelp home page GithubHelp logo

react-use's Introduction



πŸ‘
react-use





npm package CircleCI master npm downloads demos
Collection of essential React Hooks. Port of libreact.
Translations: πŸ‡¨πŸ‡³ 汉语




npm i react-use












Usage β€” how to import.
Unlicense β€” public domain.
Support β€” add yourself to backer list below.






Contributors








react-use's People

Contributors

adesurirey avatar ankithkonda avatar artywhite avatar ayush987goyal avatar belco90 avatar brickspert avatar clementparis016 avatar cleverjam avatar ddaza avatar dependabot[bot] avatar dosant avatar gelove avatar granipouss avatar jakapatb avatar kachkaev avatar kusstar avatar liuyuchenzh avatar lvl99 avatar mrhuangjser avatar nullvoxpopuli avatar petterive avatar realdennis avatar renovate-bot avatar renovate[bot] avatar semantic-release-bot avatar streamich avatar wardoost avatar xobotyi avatar ythecombinator avatar zaguiini avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-use's Issues

Unlicenced

Explicitly defining the licence will ensure there are no future developer concerns about whether or not this module is for them. May I suggest a permissive license such as MIT or Apache?

useTranslate() for component localization ?

An idea for lazy component localization with code splitting.

Suppose we want to localize component named UserProfile

// UserProfile.jsx
const UserProfile = ({ firstName, lastName }) => {
   return (
    <article>
      Hello, {firstName}!
      <div>First Name: {firstName}</div>
      <div>Last Name: {lastName}</div>
      <footer>
        Full Name: {firstName} {lastName}
      </footer>
    </article>
  );
};

At first we should define dynamically imported translations file per language:

// UserProfile.rus.jsx
export default {
  "First Name": "Имя",
  "Last Name": "Ѐамилия",
  "Hello, ${name}!": name => `ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ${name}!`,
  footer: (firstName, lastName) => (
    <footer>
      ПолноС Имя: {firstName} {lastName}
    </footer>
  )
};

Translation file is plain object, which values are strings or functions returning strings or even JSX markup.

Then we create React Context for locale LocaleContext = React.createContext() and new hook named useTranslate. It loads translations file on every context change and returns translation function to a component.

// UserProfile.jsx
const UserProfile = ({ firstName, lastName }) => {
  // translation function
  const tr = useTranslate(lang =>
    import(/* webpackChunkName: "i18n-" */ `./UserProfile.${lang}.jsx`)
  );

  return (
    <article>
      {tr`Hello, ${firstName}!`}
      <div>
        {tr`First Name`}: {firstName}
      </div>
      <div>
        {tr`Last Name`}: {lastName}
      </div>
      {tr("footer", firstName, lastName) || (
        <footer>
          Full Name: {firstName} {lastName}
        </footer>
      )}
    </article>
  );
};

Translation function can work in two modes:

  • As ES6 template tag it extracts key and arguments from passed template, and also returns raw ES6 template value as fallback if key is not found in translations object.
  • As regular function it uses first argument for key lookup and passes rest arguments to translation template.

Benefits:

  • We can localize each component independently.
  • Or we can create common resources and utilities and import them in each component translations file.
  • Or we can group translations in other ways.

Implementation can be found here: gist (about 100 SLOC).
If this is interesting, I can submit a PR.

[Maybe bug] use-css component twinkles

Some of my pages and components use use-css to style, but when I switched to them, them would always twinkle at first, without styles and become normal in the next flash.

version: react-use 5.7.1

useDebounce types improvement

I would like to propose a better typed version of your useDebounce, here my actual step:

import { useEffect, useRef } from 'react';
import cloneDeep from 'lodash/fp/cloneDeep';
import noop from 'lodash/fp/noop';

// ------------------------------------------------------------ # Type aliases #

type AnyCallback = (...args: any[]) => any;
type Debounce<Callback extends AnyCallback> = (...params: Parameters<Callback>) => void;

// -------------------------------------------------------------------- # Hook #

export default function useDebounce() {
  return <Callback extends AnyCallback>(callback: Callback, delay = 250) => {
    const timer = useRef<NodeJS.Timeout>(null);
    const debounce = useRef<Debounce<Callback>>(noop);

    useEffect(() => {
      debounce.current = (...params: Parameters<Callback>) => {
        const paramsCopy = cloneDeep(params);

        if (timer.current) {
          clearTimeout(timer.current);
        }

        // @ts-ignore
        // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065
        timer.current = setTimeout(() => callback(...paramsCopy), delay);
      };
    }, []);

    return debounce.current;
  };
}

Usage:

import useDebounce from './path/to/useDebounce'

function Component() {
  const debounce = useDebounce()

  function handleClick(event: React.ChangeEvent<HTMLInputElement>) {
    console.log('Clicked with debounce')
  }

  return <input type="text" onChange={debounce(handleClick)} />
}

Any remark will be highly appreciated!

useDropArea

Convert DropArea to useDropArea hook.

const [el] = useDropArea(<div/>, {
  onFiles: () => {},
  // ...
});

or

const ref = useRef();
useDropArea(ref, {
  onFiles: () => {},
  // ...
});

useError

Hook that creates a dispatcher for errors, that can be caught with error boundaries.

const useError = () => {
  const [err, raise] = useState();

  if (err) {
    throw err;
  }

  return raise;
};

const Demo = () => {
  const raise = useError();

  useEffect(() => {
    setTimeout(() => {
      raise(
        new Error("Could not work with setTimeout for some reason!")
      );
    }, 1000);
  });

  return <div>Hello world</div>;
};

Alternative names: useError, useRaise, useThrow, useErrorDispatch.

Seen here: reactjs/rfcs#84 🧐

Tests

  • Start adding unit tests for hooks.
  • What are the best practices for testing React Hooks?

Pending tests to be added

  • State block
    • createMemo
    • createReducer
    • useBoolean
    • useCounter
    • useNumber
    • useGetSet
    • useGetSetState
    • useList
    • useMap
    • useObservable
    • usePrevious
    • useSetState
    • useToggle
    • useDefault
  • Animation block
    • useRaf
    • useSpring
    • useTimeout
    • useTween
    • useUpdate
    • useTimeoutFn
    • useInterval
  • Lifecycle block
    • useDeepCompareEffect
    • useEffectOnce
    • useIsomorphicLayoutEffect
    • useLifecycles
    • useLogger
    • useMount
    • useMountedState
    • usePromise
    • useRefMounted
    • useUnmount
    • useUpdateEffect

useDebounce should recompute on delay change

Currently useDebounce only creates a new timeout if args change. Is there any reason for this?

I tried adding delay to args like this:

useDebounce(() => setDebouncedState(state), delay, [state, delay]);

But this causes an additional (= unnecessary) rerender.


TL;DR Scroll down to EDIT 4


I'm trying to create a useDebouncedState hook, with dynamic debounce, like this:

const useDebouncedState = <T>(initialState: T): [T, T, (state: React.SetStateAction<T>, delay?: number) => void] => {
    const [delay, setDelay] = useState(0);
    const [state, setState] = useState(initialState);
    const [debouncedState, setDebouncedState] = useState(initialState);

    const updateDebouncedState = useCallback(() => {
        if(state !== debouncedState) {
            setDebouncedState(state);
        }
    }, [state, debouncedState]);

    useDebounce(updateDebouncedState, delay, [state]);

    const updateState = useCallback((nextState: React.SetStateAction<T>, delay: number = 0) => {
        if(nextState instanceof Function) {
            nextState = nextState(state);
        }

        setDelay(delay);
        setState(nextState);
        if(delay <= 0) {
            setDebouncedState(nextState);
        }
    }, [state]);

    return [state, debouncedState, updateState];
}

It works fine, as long as the state changes. But it won't trigger when I pass equal state with a new delay to updateState.

What it's intended to do:

  • store a whole object
  • some state changes should be debounced (for example search query input). So here I'd use setState({...state, query: ''}, 300).
  • other state changes should trigger immediately (for example click on the search button, or clicking a checkbox). And here: setState(state, 0) or setState({...state, checkbox: true}, 0).
  • state is the current state, which can be used as value for input fields
  • debouncedState can be used with useEffect to detect, when to trigger an action (for example execute API request)

Test code:

const [state, debouncedState, setState] = useDebouncedState("foo");

useEffect(() => {
    const s = "bar";

    setTimeout(() => {
        setState(s, 5000);
    }, 0);
    setTimeout(() => {
        setState(s, 0); // this won't do anything, because only delay changed
    }, 1000);
}, [])

EDIT: Maybe you could also add an optimization:

if(ms < 1) {
    fn.bind(null, args)();
} else {
    const t = setTimeout(fn.bind(null, args), ms);
    setTimeoutVar(t);
}

This prevents rerender, when using zero (or negative) delay.

Btw: Is there any need for using bind instead of simply fn(args)?


EDIT 2: Here's what all my changes look like:

  const useDebounce = (fn: () => any, ms: number = 0, args: Array<any> = []) => {
    const [timeout, setTimeoutVar] = useState<any>(null);
  
    useEffect(() => {
      // if args change then clear timeout
      clearTimeout(timeout);
      if(ms < 1) { // optimization for zero-delay
        fn.bind(null, args)();
      } else {
        const t = setTimeout(fn.bind(null, args), ms);
        setTimeoutVar(t);
      }
    }, [ms, ...args]); // here I added `ms`
  };

EDIT 3: Another optimization would be to remove const [timeout, setTimeoutVar]. Because setTimeoutVar(t); will trigger a rerender, too.

My tests showed that this code for useDebounce rerenders less:

  const useDebounce = (fn: () => any, ms: number = 0, args: Array<any> = []) => {
    useEffect(() => {
      // if args change then clear timeout
      let t: any;
      if(ms < 1) {
        fn.bind(null, args)();
      } else {
        t = setTimeout(fn.bind(null, args), ms);
      }

      return () => { // use this callback for clearTimeout, it has been
        clearTimeout(t); // suggested by Dan Abramov himself:
      } // https://twitter.com/dan_abramov/status/1060729512227467264
    }, [ms, ...args]);
  };

Though I'm not sure if it's breaking change? But it seems to works fine.


EDIT 4: Forget (nearly) everything I said πŸ˜†

It looks like setTimeoutVar(t); is the only real problem that causes unnecessary rerenders!

So... This code seems to work fine in combination with useDebounce(() => ..., delay, [state, delay]):

  const useDebounce = (fn: () => any, ms: number = 0, args: Array<any> = []) => {
    useEffect(() => {
      let handle = setTimeout(fn.bind(null, args), ms);

      return () => {
        // if args change then clear timeout
        clearTimeout(handle);
      }
    }, args);
  };

usePureState

Use the same as useState but with shallow compare

TypeScript build fails

I’ve enabled the strict option in my tsconfig.json, and installing this package results in an error because it can’t find types for the useCallbag hook, even though I don’t use that hook. It seems to be trying to import the types for the use-callbag package, which don’t exist. Can you fix this error?

Issue with SSR?

I tried to use the package on next.js and it throws the following error:

Error: Addon "cssom" is missing the following dependencies:
 require("nano-css/addon/sh").addon(nano);
    at warnOnMissingDependencies (/Users/rafael/Code/blumpa-ssr/node_modules/nano-css/addon/__dev__/warnOnMissingDependencies.js:23:15)
    at exports.addon (/Users/rafael/Code/blumpa-ssr/node_modules/nano-css/addon/cssom.js:5:55)
    at Object.<anonymous> (/Users/rafael/Code/blumpa-ssr/node_modules/react-use/lib/useCss.js:27:1)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/rafael/Code/blumpa-ssr/node_modules/react-use/lib/index.js:17:18)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)

Remove large dependencies

This library minified is almost 52kb, which is huge for what it is. 60% of the bundle size is solely based on dependencies, all of which are only used in a few hook.

I would suggest making turning those into peerDependencies and requiring users to include the libraries in their own package.json. This would make the size of this library ~21kB.

Although tree shaking should take care of this, not all environments support it, and forcing dependencies on users isn't the best course of action, especially if it's for a small functionality.

If this is a go, I could make the PR with the changes (code & docs) required for this.

useUserMedia

Here's a plain Javascript version that I'm using in my application:

function useUserMedia(constraints, errorCallback=(()=>undefined)) {
    const [stream, setStream] = useState();

    useEffect(_ => {
        navigator.mediaDevices.getUserMedia(constraints)
            .then(setStream)
            .catch(errorCallback)
    }, [stream]);

    return stream;
}

To get user's front camera and audio feed:

const constraints = {
    video: true,
    audio: true
};

const onError = (e) => alert(e);

const localstream = useUserMedia(constraints, onError);

useDistinct

I'd like to have some useDistinct hook, which takes a value (or another hook) and only changes state if the returned value has changed.

It's kinda the opposite way of useMemo. It should rerender based on output instead of input value.

I'm not sure if that's possible at all...

Usage:

const compareFn = (oldValue, newValue) => {
    return oldValue[0] === newValue[0];
}

const MyComponent = (props) => {
    const [state, setState] = useDistinct(useState("foo"), compareFn);
  
    console.log("rerendered", state)

    return (
      <button onClick={() => setState("foo")}>Change State</button>
    )
}

Explanation:
When not using useDistinct, every call to setState will rerender the component. Even if you call setState(state).
When using useDistinct, it should check for === equality. Or you can provide a custom comparison function which does the equality check.

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this πŸ’ͺ.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Invalid npm token.

The npm token configured in the NPM_TOKEN environment variable must be a valid token allowing to publish to the registry https://registry.npmjs.org/.

If you are using Two-Factor Authentication, make configure the auth-only level is supported. semantic-release cannot publish with the default auth-and-writes level.

Please make sure to set the NPM_TOKEN environment variable in your CI with the exact value of the npm token.


Good luck with your project ✨

Your semantic-release bot πŸ“¦πŸš€

Adding sessionStorage - API options

Would like to add useSessionStorage.

Options API-wise:

  1. Just copy-paste useLocalStorage and s/local/session
  2. In useLocalStorage.ts, add a parameter called storageKey that defaults to 'localStorage' and have useSessionStorage just call that. Then in useLocalStorage, s/localStorage/window[storageKey]

What would you prefer?

improve/extend async state for useAsync and related hooks

just like Query Mutation in react-apollo:

<Query query={gql_document} variables={variables}>
  {({ data, error, loading }) => <div></div>}
</Query>

<Mutation mutation={gql_document}>
  {(mutation_api, mutation_result) => {
    const { data, error, loading } = mutation_result;
    return <div></div>
  }}
</Mutation>

And now we have useAsync, but it can only act as Query.

We should have an useApi to act as Mutation.

// A very very naive implementation
const useApi = (promisified_api, variables) => {
   const [state, set_state] = useState({loading: !!variables});
   const api = async (...variables) => {
    set_state({loading:true});
    const data = await promisified_api(...variables);
    set_state({data});
  }
  // we should expose `set_state`, it can act as Query's updteQuery, and use can set_state({error:null}) manually when close the error dialog.
  return [api, state, set_state];
}

const [api, state, set_state] = useApi(fn_return_promise, maybe_variables);

useStyles

Hey there!

Saw that you already got use-styles on npm, but couldn't find anything on GitHub.

Are you actively working on something? Would be keen to share experiences, since I have been working on something very similar: https://github.com/andywer/react-usestyles

Cheers!

Configure ESM build

It would be great to support ESModules to provide better tree shaking, rollup might be helpful

P.S. I mean mjs format

usePagination / usePaginator

What about that possible hook?

type HookProps =  [number, (position: number): number, ?number];

type HookParams = {
    pageSize: number, 
    collectionSize: number,
    initialCurrentPage: ?number = 1,
}

function ListComponent() {
    const params: HookParams = { 
        pageSize, 
        collectionSize,
        initialCurrentPage,
    };

    const [currentPage, gotoPage, totalPages]: HookProps = usePaginator(params);
  
    const gotoNext = (): number => gotoPage(currentPage + 1)
    const gotoPrev = (): number => gotoPage(currentPage - 1)

    return (
        <Pagination {...} />
    )
}

useLocalStorage

Add useLocalStorage hook.

const [value, setValue] = useLocalStorage('key', initialValue);

Lifecycles

Add React Hooks that can be used component lifecycles.

useForm

Any idea on useForm with (async) validation as a hook ?

cancelAnimationFrame may not work well in useRaf hook

I think the logic here may not let current animation stop perfectly, which may lead to memory leak or related things.

https://github.com/streamich/react-use/blob/master/src/useRaf.ts#L28-L30

As cancelAnimationFrame method is executed in the clean-up period, the animation ID "stored" in raf can be perfectly cancelled. However, the onFrame method may continues (especially when the main thread render DOM slowly, and it will take sometime between useEffect doing cleaning work and your requestAnimationFrame method's running, https://github.com/streamich/react-use/blob/master/src/useRaf.ts#L9 ), so that a new raf will be assigned, even though you already canvel previous raf.

In this way, you don't clean up previous animation, and next effect will bring in a new animation, which is the problem.

In my opinion, a safe way to guarantee current animation's stop is to add conditions in your main loop method, onFrame method can be rewrited to:

const onFrame = () => {
      const time = Math.min(1, (Date.now() - start) / ms);
      set(time);
      if (YOUR_CANCEL_CONDITION) {
		stop();
	  } else {
        loop();
	  }
};

const stop = () => {
	cancelAnimationFrame(raf);
}

in which YOUR_CANCEL_CONDITION is the logic you should handle with.

useLocation will break on IE11 without Event polyfill

This issue is just to note that useLocation will break on IE11 without a polyfill for window.Event. even when <meta http-equiv="x-ua-compatible" content="IE=edge"> is already in head.

IE9, 10 and 11 will break right here when new Event('pushstate'); is run by calling history.push('/something') in your react app.

const event = new Event(method.toLowerCase());

Object doesn't support this action

You need to polyfill window.Event which import '@babel/polyfill'; is not doing for you. So here goes what I've added to my polyfill.js

/**
 * To detect you are in IE (for this case) by checking typeof(Event) which is
 * 'function' for all except IE where it is 'object'.
 * You can then safely polyfill the Event constructor using the approach above.
 * In IE11 it seems to be safe to set window.Event = CustomEvent.
 */
(function() {
    if (typeof window.Event === 'function') return false; //If not IE

    function CustomEvent(event, params) {
        params = params || {
            bubbles: false,
            cancelable: false,
            detail: undefined,
        };
        var evt = document.createEvent('CustomEvent');
        evt.initCustomEvent(
            event,
            params.bubbles,
            params.cancelable,
            params.detail
        );
        return evt;
    }

    CustomEvent.prototype = window.Event.prototype;

    window.Event = CustomEvent;
})();

The solution was provided here (accepted answer). Don't skip the comments.
https://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work

Full IE11 support

First of all, thank you for this package, it has been very useful for my projects!
I've noticed the production build it's not fully compatible with IE11.
I managed to do a "manual tree-shake" using babel-plugin-transform-imports, but i still get errors on IE11 using some hooks, e.g:

// ...
// production build of useWindowSize, IE11 won't be able to destructure this
const [state, setState] = react_1.useState({
  width: isClient ? window.innerWidth : initialWidth,
  height: isClient ? window.innerHeight : initialHeight,
});
// ...

My question is: can we add a .browserslistrc on the package, so we can get full support for older browsers in the production build?

EditorConfig require

@streamich I think it's good idea to put EditorConfig in this project to make sure all contributors have the same configuration and prevent unnecessary the code formatting change over each PR.

Cheers,
Max

useThrottle

It would be nice to have useThrottle, implemented similarly to useDebounce

state should be reset before the second call in useAsync

since after the first call,state.loading was set to false in useEffect.After then state.loading would still be false in the second call for refetching.
so maybe state should be reset in useEffect:

react_1.useEffect(
    () => {
      let mounted = true;
      set({ loading: true }); // reset state
      const promise = memoized();
      promise.then(
        value => {
          if (mounted) {
            set({
              loading: false,
              value
            });
          }
        },
        error => {
          if (mounted) {
            set({
              loading: false,
              error
            });
          }
        }
      );
      return () => {
        mounted = false;
      };
    },
    [memoized]
  );

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.