
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Stream updates to React components from ES6 generators
YSU is an experimental* library to manage asynchronous state in React. It stands for yield sequential updates, which describes the process of streaming updates to components from generators.
*minimal test coverage and not yet used in production, please use with caution.
import { update, sequence } from "ysu";
// sequence --
async function* randomQuoteSequence() {
yield update("LOADING");
try {
const res = await fetch("https://programming-quotes-api.herokuapp.com/quotes/random");
const data = await res.json();
yield update("READY", data);
} catch (error) {
yield update("FAILED", error);
}
}
// component --
const RandomQuote = props => {
const [quote, getQuote] = props.randomQuote;
useEffect(() => {
getQuote();
}, []);
return (
<>
{quote.status === "LOADING" && <p>Loading...</p>}
{quote.status === "READY" && <p>{quote.payload.en}</p>}
{quote.status === "FAILED" && <p>{quote.payload.message}</p>}
<button onClick={getQuote}>Get another quote</button>
</>
);
};
export default sequence({
randomQuote: randomQuoteSequence
})(RandomQuote);
The component is connected to the generator using the sequence higher-order component. When(ever) the generator yields an update the component (re)renders.
Each field passed to sequence is mapped to a prop which contains a pair. In this example the prop randomQuote holds the pair:
[quote, reflects the current status of the sequence, along with any data associated with that status.getQuote] a function that when called initiates the sequence.The randomQuote sequence starts when the component mounts, or when the user clicks the Get another quote button. This is a fairly straightforward example, however other examples with live demos are linked below.
The examples can be viewed online, or alternatively can be run locally by cloning the repository and running npm install and then npm start.
DevTools with time travel 🚀
Baked-in logger
Middleware support
Subscribe to a stream of updates for third-party interactions such as logging and error reporting.
Much of what makes UI programming difficult is managing values that change over time. If we take the sterotypical async example of making an API request, the UI reflects a sequence of state changes.
The state starts off idle → then goes to loading* → then finishes with success or failed**.
*usually triggered on component mount / user interaction. **depending on the API response.
Our API request has three sequential stages with four possible statuses.
Async generators (async function*) are very good at coordinating sequences since they can be paused, exited and resumed.
This means we can do something asynchronous → yield an update to the UI → re-enter to resume where we left off → do something else → yield another update to the UI → and so forth.
The API request can be represented using a sequence diagram:
| Time | Component | Generator |
|---|---|---|
| ↓ | initiate sequence ------> | |
| ↓ | <------ yield "loading" | |
| ↓ | loading... | call API |
| ↓ | ✅success | <------ yield "success" on resolve |
| ↓ | ❌error | <------ yield "failed" on reject |
You may have noticed this sequence diagram depicts exactly what is happening in the basic example shown earlier.
Since yielding from a generator triggers a (re)render in the UI, and generators can generate values forever, implementing infinite and finite sequences such as polling or retries is trivial.
Polling is as simple as calling an endpoint and yielding an update to the UI within an infinite loop. Whilst retrying an XHR request does the same but from within a finite loop.
Note: on component unmount any running sequences are stopped and scheduled updates cancelled automatically. This ensures that infinite sequences (such as polling) only run when the component is mounted.
YSU exports the following functions:
import { sequence, update, pause } from "ysu";
sequenceHigher-order component that connects a component to one or more generators:
sequence(generatorMap, middleware?)(Component);
generatorMapObject that maps generator functions to component props:
sequence(
{
foo: fooSequence,
bar: barSequence
}
)(Component);
In this example fooSequence will be available in the component via the prop foo. Each prop (such as foo and bar) is an array containing 3 items:
const [value, initiator, goodies] = props.foo;
value Object containing:
status String: the status of the sequence (i.e: "LOADING")payload? Any: data associated with the current status (i.e: { userName: "@chart" })initiator Function: that starts the sequencegoodies Object containing:
devTools Component: that renders the history of the sequence. Note: the history is transient and destroyed when the component unmounts.suspend Function: that stops the sequence and cancels any scheduled updates (i.e: click button to stop polling)middlewareOne or more functions that are subscribed to the sequence. They are called whenever a generator yields an update:
sequence(
{
foo: fooSequence,
bar: barSequence
},
trackingMiddleware,
errorMiddleware,
// as many middlewares you desire
)(Component);
function trackingMiddleware({ status, payload, meta }) {
if (status === "SUCCESS") {
// track conversion
}
}
function errorMiddleware({ status, payload, meta }) {
if (status === "FAILED") {
// log error
}
}
Each middleware function is passed an object containing:
status String: the status of the sequencepayload? Any: data associated with the current statusmeta Object containing:
sequenceId String: corresponds to the name of the prop (i.e: foo or bar)Note: middleware is colocated with components since sequences could have different tracking or error logging requirements depending on where they’re used.
updatePure function that describes a step in a sequence:
yield update(status, payload?);
status String: the status of the sequencepayload? Any: data associated with the current statusReturns an object containing { status, payload? }.
pauseHelper function that pauses a sequence for a given amount of time:
await pause(delay);
delay Number: how long in milliseconds the sequence should be pausedReturns a Promise.
Only include devTools and logger in development bundles.
Expose back/forward functions for UIs with undo/redo:
const [quote, getQuote, { goBack, goForward }] = props.quoteSequence;
Persist context between yields. This would be useful when composing multiple sequences:
// sequence --
yield update("READY", { rates }, { persist: true });
// ...yields from other generators...
// component --
const [{ status, payload }, transition, { context }] = props.currencyConverter;
<>{context.rates}</> // rates available in context for any statuses that follow READY
Automatically suspend a running sequence when new sequence is initiated.
Investigate how YSU would integrate with React suspense and concurrent mode.
In-built cache and sequence deduplication.
Hooks alternative to higher-order component API:
const [quote, getQuote, goodies] = useYSU(randomQuoteSequence);
// with unique key for in-built cache: `useYSU('key', randomQuoteSequence)`
Move devTools and logger out into separate package @ysu/devtools.
0.0.0-alpha → 0.0.0-betaMIT
FAQs
Stream updates to React components from ES6 generators
We found that ysu 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.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.