Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
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.)
Furthermore, because ref.current
will either return a value or throw a promise, it 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 implement getSnapshot
to always return the result of ref.current
. A sample implementation of what you might do is the following.
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, it can 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 interacting with the
Suspense
/ErrorBoundary
portions of your component hierarchy.
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);
Finally, AsyncMutableRefObject<T>
also implements PromiseLike<T>
which means that you can also 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 1,914 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’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.