All Projects → donavon → Use Instance

donavon / Use Instance

Licence: mit
A custom React Hook that provides a sensible alternative to useRef for storing instance variables.

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Use Instance

Pullstate
Simple state stores using immer and React hooks - re-use parts of your state by pulling it anywhere you like!
Stars: ✭ 683 (+2339.29%)
Mutual labels:  hooks
React Latest Framework
a client framework of React
Stars: ✭ 835 (+2882.14%)
Mutual labels:  hooks
Genesis Simple Hook Guide
WordPress plugin that displays names of all Genesis hooks on the current page dynamically.
Stars: ✭ 25 (-10.71%)
Mutual labels:  hooks
Nbstripout
strip output from Jupyter and IPython notebooks
Stars: ✭ 738 (+2535.71%)
Mutual labels:  hooks
Awesome React Hooks
Awesome React Hooks
Stars: ✭ 7,616 (+27100%)
Mutual labels:  hooks
Usestatewithlayoutanimation
Abstraction for `React Native`'s `LayoutAnimation` and `useState`
Stars: ✭ 19 (-32.14%)
Mutual labels:  hooks
Makin
makin - reveal anti-debugging and anti-VM tricks [This project is not maintained anymore]
Stars: ✭ 645 (+2203.57%)
Mutual labels:  hooks
My React Hooks
React/Preact/Orby.js Hooks I used in personal projects, all in public domain form. Copy and paste or do as you wish.
Stars: ✭ 14 (-50%)
Mutual labels:  hooks
Web3 React
🧰 A simple, maximally extensible, dependency minimized framework for building modern Ethereum dApps
Stars: ✭ 788 (+2714.29%)
Mutual labels:  hooks
Awesome React Hooks
A curated list about React Hooks
Stars: ✭ 932 (+3228.57%)
Mutual labels:  hooks
Blockhook
Hook Objective-C blocks. A powerful AOP tool.
Stars: ✭ 742 (+2550%)
Mutual labels:  hooks
React Async Hook
React hook to handle any async operation in React components, and prevent race conditions
Stars: ✭ 781 (+2689.29%)
Mutual labels:  hooks
Pre Commit Terraform
pre-commit git hooks to take care of Terraform configurations
Stars: ✭ 902 (+3121.43%)
Mutual labels:  hooks
Formik
Build forms in React, without the tears 😭
Stars: ✭ 29,047 (+103639.29%)
Mutual labels:  hooks
Fisher
Simple yet powerful webhooks catcher
Stars: ✭ 11 (-60.71%)
Mutual labels:  hooks
Awesome Git Hooks
⚓️ A curated list of awesome git hooks
Stars: ✭ 661 (+2260.71%)
Mutual labels:  hooks
Rooks
Essential hooks ⚓ to super charge your components!
Stars: ✭ 889 (+3075%)
Mutual labels:  hooks
Tinytcpserver
A small tcp server working under Mono or .NET (4.0) and provides hooks for handling data exchange with clients (works under mono and .net). Behaviour/protocol/reaction could be specified via custom C# script.
Stars: ✭ 14 (-50%)
Mutual labels:  hooks
Axios Hooks
🦆 React hooks for axios
Stars: ✭ 862 (+2978.57%)
Mutual labels:  hooks
Flagchecker
For effective cheating detection in competitions. Utilizes Linux Kernel Module (LKM) for generating flags.
Stars: ✭ 24 (-14.29%)
Mutual labels:  hooks

@use-it/instance 🐘

A custom React Hook that provides a sensible alternative to useRef for storing instance variables.

npm version All Contributors

Why?

useRef is weird. The official React docs say:

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

Note that useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.

The fact that you have to access it via .current is strange. The fact that the React docs call out that you can use it for more than ref attributes is telling. They have to know that useRef is confusing too.

That's why I created useInstance. You treat the object returned by useInstance just like you would this in a class component, so you're already familiar with how it works.

So… Use useRef if you're dealing with actual DOM elements—use useInstance for instance properties and methods.

Some may say "Six of one, hald dozen of another," and they could be right. But if you're in the "half-dozen" camp, useInstance might well be for you!

Want more evidence that useRef is weird? In the latest React v17 RC under "Potential Issues" it shows that this code could break.

useEffect(() => {
  someRef.current.someSetupMethod();
  return () => {
    someRef.current.someCleanupMethod();
  };
});

The solution that they are suggesting (below) is to use an instance variable, which is exactly what useInstance is doing. Had React implimented useInstance this "potential issue" would never have been raised.

useEffect(() => {
  const instance = someRef.current;
  instance.someSetupMethod();
  return () => {
    instance.someCleanupMethod();
  };
});

Installation

$ npm i @use-it/instance

or

$ yarn add @use-it/instance

Usage

Here is a basic setup. Most people will name the returned object self or that or instance, but you can call it anything you'd like.

import useInstance from '@use-it/instance';

// Then from within your function component
const instance = useInstance(initialInstance);

Parameters

Here are the parameters that you can use. (* = optional)

Parameter Description
initialInstance Is an object or a function that returns an object that represents the initial instance value. Defaults to an empty object literal.

Return

The return value is an object that WILL NOT change on subsequent calls to your function component. Use it just like you would to create instance properties and methods in a class component.

Examples

A replacement for useRef

Here's an example where you might use useRef. Within the closure of the useEffect, if we simply reference callback directly, we will see callback as it was during the creation of the function. Instead we must make it "live" throughout many render cycles.

function useInterval(callback, delay) {
  const savedCallback = useRef();
  savedCallback.current = callback;

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

And here's the same code using useInstance. It has a more familiar class component-like syntax. Again, think of instance as this.

function useInterval(callback, delay) {
  const instance = useInstance();
  instance.savedCallback = callback;

  useEffect(() => {
    function tick() {
      instance.savedCallback();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

A replacement for useCallback/useMemo

You can also use useInstance in place of useCallback and useMemo for some cases.

Take for instance (pardon the pun), this simple up/down counter example. You might use useCallback to ensure that the pointers to the increment and decrement function didn't change.

const useCounter = initialCount => {
  const [count, setCount] = useState(initialCount);
  const increment = useCallback(() => setCount(c => c + 1));
  const decrement = useCallback(() => setCount(c => c - 1));

  return {
    count,
    increment,
    decrement,
  };
};

Or, you could store them in the instance, much like you might have done with a class component.

const useCounter = initialCount => {
  const [count, setCount] = useState(initialCount);
  const self = useInstance({
    increment: () => setCount(c => c + 1),
    decrement: () => setCount(c => c - 1),
  });

  return {
    count,
    ...self,
  };
};

What is the benefit of keeping functions in instances variable instead of using useCallback? This is from the official Hooks documentation.

In the future, React may choose to “forget” some previously memoized values and recalculate them on next render.

Instance variables never forget. 🐘

Lazy initialization

If computing the initialInstance is costly, you may pass a function that returns an initialInstance. useInstance will only call the function on mount.

Not that creating two functions is expensive, but we could re-write the example above using a lazy initializer, like this.

// this is only called once, on mount, per component instance
const getInitialInstance = setCount => ({
  increment: () => setCount(c => c + 1),
  decrement: () => setCount(c => c - 1),
});

const useCounter = initialCount => {
  const [count, setCount] = useState(initialCount);
  const self = useInstance(getInitialInstance(setCount));

  return {
    count,
    ...self,
  };
};

Notice that we moved getInitialInstance into a static fucntion outside of useCounter. This helps to reduce the complexity of useCounter. An added side effect is that getInitialInstance is now highly testable.

You might even take this one step further and refactor your methods into it's own custom Hook. Isn't coding fun? 😊

const getInitialInstance = setCount => ({
  increment: () => setCount(c => c + 1),
  decrement: () => setCount(c => c - 1),
});

const useCounterMethods = setCount => useInstance(getInitialInstance(setCount));

const useCounter = initialCount => {
  const [count, setCount] = useState(initialCount);
  const methods = useCounterMethods(setCount);

  return {
    count,
    ...methods,
  };
};

Live demo

You can view/edit the "you clicked" sample code above on CodeSandbox.

Edit demo app on CodeSandbox

License

MIT Licensed

Contributors

Thanks goes to these wonderful people (emoji key):


Donavon West

🤔 🚇 🚧 👀 💻 🎨

Derek Jones

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].