Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Async ref objects for React. A tiny bridge between React.useSyncExternalStore and React.Suspense.
This library allows you to create asynchronous ref objects (AsyncMutableRefObject<T>
). Similar to the sync ref objects you'd get back from useRef<T>()
, except if the current value of the ref (ref.current
) is not set, it will throw a Promise
instead of returning nothing.
React.Suspense
works by catching promises—thrown as part of rendering a component—and waiting for them to resolve before re-rendering again. Assigning a value to ref.current
will trigger the suspense boundary to re-render. (You can read more about how Suspense works in the React docs.)
Because ref.current
will either return a value T
or throw a promise, the only thing it can return is a T
and therefore implements the MutableRefObject<T>
interface. That is,
type MutableRefObject<T> = {
current: T;
};
class AsyncMutableRefObject<T> implements MutableRefObject<T> {
// ...
}
React v18 introduces the experimental hook useSyncExternalStore
which provides a convenient way to hook into synchronous external data sources.
declare function useSyncExternalStore<Snapshot>(
subscribe: (onStoreChange: () => void) => () => void,
getSnapshot: () => Snapshot,
getServerSnapshot?: () => Snapshot
): Snapshot;
AsyncMutableRefObject<T>
makes it easy for a typed asynchronous external data source to work with React Suspense in a typed synchornous way because we can implement getSnapshot
to just always return the result of ref.current
.
A sample implementation of what you might do is the following where we define a data source that implements getSnapshot
and subscribe
functions.
type Subscriber = () => void;
class AsyncDataStore<T> {
subscribers = new Set<Subscriber>();
ref = createAsyncRef<T>(() => this.notify());
getSnapshot = (): T => {
return this.ref.current;
};
subscribe = (sub: Subscriber): Subscriber => {
this.subscribers.add(sub);
return () => this.subscribers.delete(sub);
};
notify() {
this.subscribers.forEach((sub) => sub());
}
doSomething() {
fetch(/*...*/)
.then((res) => res.json())
.then((data) => {
// setting the current value will notify all subscribers.
ref.current = data;
});
}
}
As long as getSnapshot()
is called from within the React-render cycle, the Promise
it (might) throw will be caught by Suspense.
const store = new AsyncDataStore<User>();
// ...
const user = React.useSyncExternalStore(store.subscribe, store.getSnapshot);
// => throws Promise from the ref,
// which is caught by a Suspense boundary
// that waits for it to resolve.
// ...
store.doSomething();
// => after some time, the current value is set.
// ...
const user = React.useSyncExternalStore(store.subscribe, store.getSnapshot);
// => returns a User
async-ref
exports a single function createAsyncRef<T>()
which accepts an optional notifier function and returns an AsyncMutableRefObject<T>
.
declare function createAsyncRef<T>(notifier?: () => void): AsyncMutableRefObject<T>;
import { createAsyncRef } from "async-ref";
const ref = createAsyncRef<User>();
const currentValue: User = ref.current;
// => throws a Promise
ref.current = { id: "12345", name: "Gabe" /* etc */ };
const currentValue: User = ref.current;
// => returns { id: '12345', name: 'Gabe', /* etc */ }
Just like a MutableRefObject
, the current value can be set any number of times.
import { createAsyncRef } from "async-ref";
const ref = createAsyncRef<number>();
ref.current = 12;
ref.current = 400;
const currentValue = ref.current;
// => 400
Alternatively, AsyncMutableRefObject<T>
exposes resolve
/reject
functions for a more familiar Promise-type feel.
import { createAsyncRef } from "async-ref";
const ref = createAsyncRef<number>();
ref.reject(new Error("uh oh!"));
ref.current;
// => throws an Error("uh oh!")
If you provide a notifier function, it will be called every time the state of the ref changes.
import { createAsyncRef } from "async-ref";
const listener = jest.fn();
const ref = createAsyncRef<number>(listener);
ref.current = 12;
ref.current = 400;
expect(listener).toHaveBeenCallTimes(2);
If you want to prevent the ref from changing its state, you can freeze it.
const ref = createAsyncRef<number>(listener);
ref.current = 12;
ref.freeze();
ref.current = 400;
expect(ref.current).toEqual(12);
AsyncMutableRefObject<T>
also implements PromiseLike<T>
which means that you can dereference the current value by awaiting on it. (If a current value is already set, it will immediately resolve.) This is safer than calling ref.current
because it will wait for a current value to be set before resolving the promise, but of course does not work inside of a React component because it is asynchronous.
import { createAsyncRef } from "async-ref";
const ref = createAsyncRef<User>(listener);
// ...
const user = await ref;
yarn add async-ref
FAQs
Async ref objects for React. A tiny bridge between React.useSyncExternalStore and React.Suspense.
The npm package async-ref receives a total of 3,150 weekly downloads. As such, async-ref popularity was classified as popular.
We found that async-ref demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.