usestable
TypeScript icon, indicating that this package has built-in type declarations

0.1.25 • Public • Published

useStable

A React hook to create stable component/object

Installation

with NPM

npm i usestable --save

with YARN

yarn add usestable

Motivation

Inspired by useEvent RFC

Recipes

Stable identity callback

with useEvent

import { useEvent } from "react";

function Chat() {
  const [text, setText] = useState("");

  const onClick = useEvent(() => {
    sendMessage(text);
  });

  return <SendButton onClick={onClick} />;
}

with useStable

import { useStable } from "react";

function Chat() {
  const [text, setText] = useState("");
  const stable = useStable({
    onClick: () => {
      sendMessage(text);
    },
  });

  return <SendButton onClick={stable.onClick} />;
}

Stable component

For above examples, you must wrap your SendButton with memo(). Need to useEvent every time and easy to forget. usestable provide stable() HOC to create a stable component on the fly

import { stable } from "usestable";

const SendButton = stable((props) => {
  // implementation
});

// you also wrap 3rd-party components
import { Button } from "antd";

// even with specified props only. by default, all props will be stable
const SendButton = stable(Button, { props: ["onClick"] });

There you go, no need to wrap any event callbacks. You also free with inline callbacks

function Chat({ rooms }) {
  const [text, setText] = useState("");

  return rooms.map((room) => (
    <SendButton
      // if you wrap SendButton with stable(), you dont not worry this
      onClick={
        () => sendMessage(room, text)
        // 🙁 Can't wrap it with useEvent
      }
    />
  ));
}

Geting fresh values in callbacks

Sometimes, useEvent fails with async callback

with useEvent

const contextVariable = useContext(SomeContext);
const callback = useEvent(async () => {
  // the contextVariable is fresh now
  console.log(contextVariable);
  await callAsyncMethod();
  // but it is outdated now
  console.log(contextVariable);
});

You must use more useEvent hook to handle above case

const contextVariable = useContext(SomeContext);
const onDone = useEvent(() => {
  // do something with contextVariable
});
const callback = useEvent(async () => {
  // the contextVariable is fresh now
  console.log(contextVariable);
  await callAsyncMethod();
  onDone();
});

with useStable #1

import { useStable } from "usestable";

const contextVariable = useContext(SomeContext);
// create stable object to hold contextVariable
const stable = useStable({ contextVariable });
const callback = useCallback(async () => {
  // the contextVariable is fresh now
  console.log(stable.contextVariable);
  await callAsyncMethod();
  // and it is still fresh now and after. Easy ?
  console.log(stable.contextVariable);
}, [stable]);

with useStable #2

import { useStable } from "usestable";

const contextVariable = useContext(SomeContext);
// create stable object to hold contextVariable
const { callback } = useStable({
  // add contexture variables
  contextVariable,
  // define async callback
  async callback() {
    // using this object to access stable props
    // the contextVariable is fresh now
    console.log(this.contextVariable);
    await callAsyncMethod();
    // and it is still fresh now and after. Easy ?
    console.log(this.contextVariable);
  },
});

Using stable object with other React hooks

You can use stable object with any React's hooks

const stable = useStable({
  some,
  stable,
  variables,
  here,
  dateValue, // useStable will not update date values if its timestamp does not change
  stableCallback() {},
});

const flexibleCallback = useCallback(() => {
  console.log(unstableVar);
  console.log(stable.variables);
}, [stable, unstableVar /* add dependencies to control callback re-create */]);

useEffect(() => {
  socket.on("connected", () => {
    console.log(stable.some);
    console.log(stable.variables);
  });
}, [stable /* add more dependencies ad you need */]);

Conditional callbacks

Sometimes React memo and useEvent fail with conditional callbacks

function Chat({ onOdd, onEven }) {
  const [text, setText] = useState("");

  return <SendButton onClick={text.length % 2 ? onEven : onOdd} />;
}

You must add more useEvent hook to handle conditional callbacks

function Chat({ onOdd, onEven }) {
  const [text, setText] = useState("");
  const onClick = useEvent(() => (text.length % 2 ? onEven() : onOdd()));

  return <SendButton onClick={onClick} />;
}

If SendButton already wrapped by stable() HOC, everything done without any effort

API reference

https://linq2js.github.io/usestable/

Package Sidebar

Install

npm i usestable

Weekly Downloads

9

Version

0.1.25

License

ISC

Unpacked Size

73.5 kB

Total Files

10

Last publish

Collaborators

  • linq2js