Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
@shopify/react-effect
Advanced tools
A component and set of utilities for performing effects within a universal React app
@shopify/react-effect
This package contains a component and set of utilities for performing multiple effects during one single pass of server rendering in a universal React application.
$ yarn add @shopify/react-effect
<Effect />
This package is largely built around a component, Effect
. To set up an action to be performed, use the perform
prop:
import {Effect} from '@shopify/react-effect';
export default function MyComponent() {
return <Effect perform={() => console.log('Doing something!')} />;
}
This callback can return anything, but returning a promise has a special effect: it will be waited for on the server when calling extract()
.
This component accepts three additional optional properties:
kind
: a description of the effect. This kind, if provided, must have an id
that is a unique symbol, and can optionally have betweenEachPass
and afterEachPass
functions that add additional logic to the betweenEachPass
and afterEachPass
options for extract()
.This component also accepts children which are rendered as-is.
extract()
You can call extract()
on a React tree in order to perform all of the effects within that tree. This function repeatedly calls a render function (by default, react-dom
’s renderToStaticMarkup
), collects any Effect
promises and, if there are promises, waits on them before performing another pass. This process ends when no more promises are collected during a pass of your tree.
Note: this flow is significantly different from the previous version, which relied on a custom tree walk. Calling
extract()
no longer waits for promises collected higher in the tree before processing the rest. Instead, it relies on multiple passes, which gives application code the option to process promises at many layers of the app in parallel, rather than in sequence.
This function returns a promise that resolves when the tree has been fully processed.
import {renderToString} from 'react-dom/server';
import {extract} from '@shopify/react-effect/server';
async function app(ctx) {
const app = <App />;
await extract(app);
ctx.body = renderToString(app);
}
You may optionally pass an options object that contains the following keys (all of which are optional):
include
: an array of symbols that should be collected during tree traversal. These IDs must align with the kind.id
field on <Extract />
elements in your application.
import {renderToString} from 'react-dom/server';
import {EFFECT_ID as I18N_EFFECT_ID} from '@shopify/react-i18n';
import {extract} from '@shopify/react-effect/server';
async function app(ctx) {
const app = <App />;
// will only perform @shopify/react-i18n extraction
await extract(app, [I18N_EFFECT_ID]);
ctx.body = renderToString(app);
}
maxPasses
: a number that limits the number of render/ resolve cycles extract
is allowed to perform. This option defaults to 5
.
betweenEachPass
: a function that is called after a pass of your tree that did not "finish" (that is, there were still promises that got collected). This function can return a promise, and it will be waited on before continuing. It is called with a single argument: a Pass
object, which contains the index
, finished
, renderDuration
and resolveDuration
of the just-completed pass.
afterEachPass
: a function that is called after each pass of your tree, regardless of whether traversal is "finished". This function can return a promise, and it will be waited on before continuing. This function is called with the same argument as the betweenEachPass
option.
decorate
: a function that takes the root React element in your tree and returns a new tree to use. You can use this to wrap your application in context providers that only your server render requires.
import {renderToString} from 'react-dom/server';
import {extract} from '@shopify/react-effect/server';
import {createApolloBridge} from '@shopify/react-effect-apollo';
async function app(ctx) {
const ApolloBridge = createApolloBridge();
const app = <App />;
await extract(app, {
decorate(element) {
return <ApolloBridge>{element}</ApolloBridge>;
},
});
ctx.body = renderToString(app);
}
renderFunction
: an alternative function to renderToStaticMarkup
for traversing the tree.
A common mistake is initializing a provider entirely within your application component, and setting some details on this provider during the extraction. There is nothing implicitly wrong with this, but it will usually not have the effect you are after. When you call renderToString()
to actually generate your HTML, the app will be reinitialized, and all of the work you did in the extraction call will be lost. To avoid this, pass any "stateful" managers/ providers into your application:
class StatefulManager {}
const {Provider, Consumer} = React.createContext();
// bad
export default function App() {
return (
<Provider value={new StatefulManager()}>
<Consumer>
{manager => <Effect perform={() => (manager.value = true)} />}
</Consumer>
</Provider>
);
}
const app = <App />;
await extract(app);
// All your work is lost now, because the components are reinitialized
renderToString(app);
// good
export default function App({manager}) {
return (
<Provider value={manager}>
<Consumer>
{manager => <Effect perform={() => (manager.value = true)} />}
</Consumer>
</Provider>
);
}
const manager = new StatefulManager();
const app = <App manager={manager} />;
await extract(app);
// All your work is preserved, because you passed in the same manager
renderToString(app);
FAQs
A component and set of utilities for performing effects within a universal React app
The npm package @shopify/react-effect receives a total of 31,996 weekly downloads. As such, @shopify/react-effect popularity was classified as popular.
We found that @shopify/react-effect demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers 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
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.