
Research
Security News
The Growing Risk of Malicious Browser Extensions
Socket researchers uncover how browser extensions in trusted stores are used to hijack sessions, redirect traffic, and manipulate user behavior.
Amorphous makes sharing state in react as easy as using setState
.
Just as this.state
is a component's state and can be updated with
setState
, this.appState
is an app's state and can be updated with
this.setAppState
:
class Input extends AppComponent {
render() {
return (
<input
type="text"
value={this.appState.text}
onChange={(e) => this.setAppState({ text: e.target.value })}
/>
);
}
}
Amorphous is designed to:
Amorphous has two main classes:
AppComponent
: a class for components that use appStateRootAppComponent
: a class for the root component of your appTo use AppComponent
, you must have a RootAppComponent
at the
root of your app. (For library authors, see
using Amorphous in a library.)
Both AppComponent
and RootAppComponent
have access to:
this.appState
this.setAppState
shouldComponentUpdate(nextProps, nextState, appState)
componentDidUpdate(nextProps, nextState, snapshot, appState)
import { AppComponent, RootAppComponent } from 'amorphous';
class Input extends AppComponent {
render() {
return (
<input
type="text"
value={this.appState.text || 'null'}
onChange={(e) => this.setAppState({ text: e.target.value })}
/>
);
}
}
class Output extends AppComponent {
render() {
return (
<span>
{'You typed: '}
{this.appState.text}
</span>
);
}
}
class App extends RootAppComponent {
appState = { text: 'hi' };
render() {
return (
<div>
<Input />
<Output />
</div>
);
}
}
First, install Amorphous by running:
npm install amorphous
Then, you can import AppComponent
and RootAppComponent
:
import { AppComponent, RootAppComponent } from 'amorphous';
At the root of your application (or subtree), extend
RootAppComponent
instead of React.Component
:
class App extends RootAppComponent {
// ...
}
And optionally initialize your appState:
class App extends RootAppComponent {
constructor(props) {
super(props);
this.appState = {text: 'hi'};
}
}
Then, in any component you want to access appState, extend AppComponent
instead of React.Component
:
class Input extends AppComponent {
// ...
}
Inside this component, you can access this.appState
and update app state
with this.setAppState
:
class Input extends AppComponent {
render() {
return <input
type="text"
value={this.appState.text}
onChange={e => this.setAppState({text: e.target.value})}
/>;
}
}
And you're ready to send shared state to anywhere your app needs it!
RootAppComponent
creates a new appState, and should be extended by your
app's root component. Any AppComponent
must be a descendent of a
RootAppComponent
(that is, all AppComponent
s must have a RootAppComponent
above them, but not necessarily directly above them, in their component tree).
RootAppComponent
is a base component, so you should extend
from it
like you would React.Component
.
class App extends RootAppComponent {
// ...
}
To initialize appState, you should set appState
either as an instance property
or in the constructor, as you would with state
:
class App extends RootAppComponent {
state = {};
appState = { someProperty: 0 };
// or:
constructor(props) {
super(props);
this.state = {};
this.appState = { someProperty: 0 };
}
}
this.appState
Initialize or access appState
. this.appState
should be initialized in your root
component's constructor (or via appState =
inside the class body).
this.setAppState(update, callback)
this.appStateContext
shouldComponentUpdate(nextProps, nextState, nextAppState)
componentDidUpdate(prevProps, prevState, snapshot, prevAppState)
AppComponent
is a replacement for React.Component
for any component that
needs access to appState
. Any AppComponent
must be a descendent of a
RootAppComponent
(that is, all AppComponent
s must have a RootAppComponent
above them, but not necessarily directly above them, in their component tree).
AppComponent
is a base component, so you should extend
from it
like you would React.Component
.
class SomeComponent extends AppComponent {
// ...
}
Your component can access this.appState
in render()
, as you would
access this.state
, and can call this.setAppState
from within any event
handlers, as you would for this.setState
.
class SomeComponent extends AppComponent {
render() {
return (
<input
type="button"
value={"clicked " + this.appState.buttonClickedCount + " times"}
onClick={() => this.setAppState({
buttonClickedCount: this.appState.buttonClickedCount + 1,
})}
/>
);
}
}
this.appState
Access appState
. this.appState
should be initialized in your root
component's constructor (or via appState =
inside the class body).
this.setAppState(update, callback)
this.appStateContext
shouldComponentUpdate(nextProps, nextState, nextAppState)
componentDidUpdate(prevProps, prevState, snapshot, prevAppState)
appState
allows all AppComponents (including the RootAppComponent) to share
state. It works similarly to this.state
, but is shared across all AppComponents.
appState
appState
is initialized by the RootAppComponent's constructor. By default it
is initialized to {}
, but you should initialize it to a more reasonable
default in your RootAppComponent class:
class App extends RootAppComponent {
appState = { myToDos: [] };
// or:
constructor(props) {
super(props);
this.appState = { myToDos: [] };
}
}
appState
Amorphous provides this.appState
in all AppComponents (including your
RootAppComponent).
NOTE: this.appState
is not accessible in the constructor of
AppComponents, or in any static methods.
This means that in render()
or other methods, you can access this.appState
to read your app's current state, and display something based on that:
class MyToDoList extends AppComponent {
render() {
return (
<div>
{this.appState.myToDos.map((todoItem) => (
<MyToDo item={todoItem} />
))}
</div>
);
}
}
appState
AppState can be updated from any AppComponent or RootAppComponent using
this.setAppState()
.
class MyToDoList extends AppComponent {
render() {
return (
<div>
{this.appState.myToDos.map((todoItem) => (
<MyToDo item={todoItem} />
))}
<input
type="button"
value="Add To-Do"
onClick={() => this.setAppState({
myToDos: this.appState.myToDos.concat({
text: '',
completed: false,
}),
})}
/>
</div>
);
}
}
appState
in lifecycle methodsAmorphous provides an additional appState
parameter to
shouldComponentUpdate
and
componentDidUpdate
for AppComponents and
RootAppComponents. This allows components to compare this.appState
to
previous/next versions of appState
.
See shouldComponentUpdate
and
componentDidUpdate
for more information.
this.setAppState(update, callback)
Like this.setState
but for app state instead of component state.
update
may be an object or a function.
If update
is an object:
setAppState
will merge update
into this.appState
If update
is a function:
update
must have the form (prevAppState) => newAppState
setAppState
will call update
with the current appState value, and will
merge the returned newAppState
value into appState
.setAppState
is not synchronous, and will call callback
after it has
completed merging update
into appState
.
Amorphous provides this.appState and this.setAppState during and after your component's first render. They are not accessible in the constructor.
Additionally, Amorphous provides an appState
parameter for the following
React lifecycle methods:
shouldComponentUpdate(nextProps, nextState, nextAppState)
componentDidUpdate(prevProps, prevState, snapshot, prevAppState)
You may use either of these methods to monitor changes to appState
and update your AppComponent
properly, like you would for this.state
.
Amorphous AppComponents and RootAppComponents provide a third parameter to
shouldComponentUpdate: nextAppState
, which indicates
the next value of appState
, so that components may avoid rendering if none
of their dependent props/state/appState have changed. See
lifecycle methods for more details and examples.
Amorphous provides this.appState and this.setAppState during and after your component's first render. They are not accessible in the constructor.
Additionally, Amorphous provides an appState
parameter for the following
React lifecycle methods:
shouldComponentUpdate(nextProps, nextState, nextAppState)
componentDidUpdate(prevProps, prevState, snapshot, prevAppState)
You may use either of these methods to monitor changes to appState
and update your AppComponent
properly, like you would for this.state
.
Amorphous AppComponents and RootAppComponents provide a fourth parameter to
componentDidUpdate: prevAppState
, which holds
the value of appState
before the most recent render, and may be useful
for comparing with the new this.appState
value to perform non-react
updates after the component has rendered. See
lifecycle methods for more details and examples.
Note: snapshot
is the return value of
getSnapshotBeforeUpdate()
, or undefined
if no getSnapshotBeforeUpdate()
is specified.
static getDerivedAppState(appState)
Similar to getDerivedStateFromProps
, Amorphous supports a static
getDerivedAppState
method on the RootAppComponent
only. This
function may be used to trigger additional modifications of appState
when appState is modified, which can be useful for caching expensive
calculations or time-unique values.
Example:
If you are a library author using Amorphous, it is important to make sure your library's appState does not conflict with the client's appState.
Amorphous uses React context to control which components have access to which appStates. To make a new context for your library, use:
import { createAppStateContext } from 'amorphous';
const MyAppStateContext = createAppStateContext();
Then, to specify that your components use MyAppStateContext
instead of
the default appState context, set the appStateContext
property on those
components:
class MyApp extends RootAppComponent {
appStateContext = MyAppStateContext;
// ...
}
class MyComponent extends AppComponent {
appStateContext = MyAppStateContext;
// ...
}
To make this less reduntant, I suggest making your own RootAppComponent and AppComponent classes for your library with extension:
import { AppComponent, RootAppComponent, createAppStateContext } from 'amorphous';
const MyAppStateContext = createAppStateContext();
export class MyAppComponent extends AppComponent {
appStateContext = MyAppStateContext;
}
export class MyRootAppComponent extends RootAppComponent {
appStateContext = MyAppStateContext;
}
Then everywhere you would use AppComponent
or RootAppComponent
, you
can instead use MyAppComponent
or MyRootAppComponent
from that file's
exports.
Amorphous has full support for flow types.
The relevant type information for RootAppComponent
and AppComponent
is:
class RootAppComponent<Props, State, AppState: Object>
extends React.Component<Props, State> {
}
class AppComponent<Props, State, AppState: Object>
extends React.Component<Props, State> {
}
To use these types, you can specify Props, State, and AppState types,
and use these in your component declarations. We recommend creating
your AppState
type in its own file, which can be included from all
your components.
import { RootAppComponent } from 'amorphous';
import type { AppState } from './my-app-state-type.js';
type Props = {
mode: string,
};
type State = { };
class App extends RootAppComponent<Props, State, AppState> {
appState: AppState = {
// ...
};
render() {
// ...
}
}
FAQs
React state management, without the new concepts
We found that amorphous 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 how browser extensions in trusted stores are used to hijack sessions, redirect traffic, and manipulate user behavior.
Research
Security News
An in-depth analysis of credential stealers, crypto drainers, cryptojackers, and clipboard hijackers abusing open source package registries to compromise Web3 development environments.
Security News
pnpm 10.12.1 introduces a global virtual store for faster installs and new options for managing dependencies with version catalogs.