useStableA React hook to create stable component/object
with NPM
npm i usestable --save
with YARN
yarn add usestable
Inspired by useEvent RFC
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} />;
}
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
      }
    />
  ));
}
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);
  },
});
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 */]);
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
Generated using TypeDoc