
Product
Introducing Webhook Events for Alert Changes
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.
OmReact is a thin layer over React that allows writing purely functional components that hold local state. React is mostly a functional framework, but it still promotes imperative code through this.setState, which works by performing side-effects.
OmReact is similar to the Elm architecture, but applied to components: define a single update function that takes events and returns actions (new state + async/parent actions). On render, event props take pure values, either event constructors or plain values (example: $onClick="increment"), instead of functions with side effects.
$ yarn add omreact
import React from 'react';
import {component, newState} from 'omreact';
const init = newState({value: 0});
const update = (event, state, props) => {
switch (event) {
case "decrement":
return newState({value: state.value - 1});
case "increment":
return newState({value: state.value + 1});
default:
throw new Error(`Unknown event: ${event}`);
}
};
const render = (state, props) => (
<div>
<button $onClick="decrement">-1</button>
<button $onClick="increment">+1</button>
<div>{state.value}</div>
</div>
);
export default component("MyCounterSimple", {init, render, update});

type Action = NewState | AsyncAction | ParentAction;
component: (
name: string,
options: {
init: Action | Props => Action,
update: (Event, State, Props) => Action | Array<Action>,
render: (State, Props) => React.Element,
lifecycles?: {newProps?: Event},
propTypes?: Object,
defaultProps?: Object,
}) => React.Component;
Options:
init: Set the initial state and async/parent actions. This replaces state = in a React class component and async and props calls in componentDidMount.
update: Takes an event, the current state and props, and returns the actions to dispatch.
render with $eventProp={Event | Args => Event}: Like a React render function except that event props must be prefixed with a $. An event can be either a plain value or a pure function. $ is being used for convenience, it's a valid character for a variable name so there is no need to use a custom JSX babel transform. @onClick={...} would be probably nicer, though.
lifecycles: More on the lifecyle section.
propTypes/defaultProps. Standard React keys, will be passed down to the component.
newState)Return the new state of the component. This should be the new full state, not partial state like this.setState takes. Function: newState: State => Action is provided.
asyncAction)In OmReact components, you don't have access to setState, to write asynchronous code (timers, requests), you return instead an async action with a promise that resolves into some other actions. An example:
import React from 'react';
import {Button} from '../helpers';
import {component, asyncAction, newState} from 'omreact';
const getRandomNumber = (min, max) => {
return fetch("https://qrng.anu.edu.au/API/jsonI.php?length=1&type=uint16")
.then(res => res.json())
.then(json => (json.data[0] % (max - min + 1)) + min);
};
const events = {
add: value => ({type: "add", value}),
fetchRandom: {type: "fetchRandom"},
};
const init = newState({value: 0});
const update = (event, state, props) => {
switch (event.type) {
case "add":
return newState({value: state.value + event.value});
case "fetchRandom":
return asyncAction(getRandomNumber(1, 10).then(events.add));
default:
throw new Error(`Unknown event: ${JSON.stringify(event)}`);
}
};
const render = (state, props) => (
<div>
<Button $onClick={events.fetchRandom}>+ASYNC_RANDOM(1..10)</Button>
<div>{state.value}</div>
</div>
);
export default component("CounterWithSideEffects", {init, render, update});
parentAction)React components report to their parents through props. While there is nothing preventing you from directly calling a prop in an OmReact component like you do in React, you should keep it purely functional by returning a a parentAction. Example:
import React from 'react';
import {Button} from '../helpers';
import {component, newState, parentAction} from 'omreact';
const events = {
increment: ev => ({type: "increment"}),
notifyParent: ev => ({type: "notifyParent"}),
};
const init = newState({value: 0});
const update = (event, state, props) => {
switch (event.type) {
case "increment":
return newState({value: state.value + 1});
case "notifyParent":
return parentAction(props.onFinish, state.value);
default:
throw new Error(`Unknown event: ${JSON.stringify(event)}`);
}
};
const render = (state, props) => (
<div>
<Button $onClick={events.increment}>+1</Button>
<Button $onClick={events.notifyParent}>Notify parent</Button>
<div>{state.value}</div>
</div>
);
export default component("CounterParentNotifications", {init, render, update});
OmReact implements those React lifecycles:
newProps: (prevProps: Props) => Event. Called any time props change.Example:
const events = {
newProps: prevProps => ({type: "newProps", prevProps}),
}
const update = (event, state, props) => (
switch (event.type) {
case "newProps":
return newState({value: event.prevProps.value});
}
);
### Events
#### Typical event signatures
A typical way of defining events is to have *constructor arguments* (optional, should be memoized), *event arguments* (should not be memoized), or both. A typical `events` object may look like this:
```js
import {memoize} from 'omreact';
const events = {
increment: {type: "increment"},
add: memoize(value => ({type: "add", value})),
addMouseButton: ev => ({type: "addMouseButton", ev}),
addValueAndMouseButton: memoize(value => ev => ({type: "add", value, ev})),
}
Use like this on the event props of rendered elements:
events.increment: An object, use it when you need no arguments. Example $onClick={events.increment}. The dispatcher will see that it's not a function and won't call it with the event arguments.events.add: A 1-time callable function that takes only event constructor arguments. Example: $onClick={events.add(1)}. This function should be memoized.events.addMouseButton: A 1-time callable function that takes only event arguments: Example: $onClick={events.addMouseButton}. This function should not be memoized.events.addValueAndMouseButton: A 2-time callable function that takes both constructor and event arguments: $onClick={events.addValueAndMouseButton(1)}. The first function should be memoized.It's well known that you should never pass newly created values as props, otherwise a React component will think those props changed and will issue an unnecessary re-render. This applies to arrays, objects or arrow functions (no problem with strings, === works fine on them). Extract prop values to const values to avoid this problem. Also, use memoization (the library already provides a helper for that: memoize) in event constructors. Example:
import {component, memoize} from 'omreact';
const events = {
increment: ev => {type: "increment"},
add: memoize(value => ({type: "increment"})),
};
An event can be any any object or function (if it has constructor/prop arguments). Create your own abstractions using events as strings, arrays, objects, proxies, whatever works for you.
Check the examples to see some alternative ways:
Using a function that builds events from a string and constructor arguments.
Using pre-defined ADT constructors.
Using on-the-fly proxy constructors.
import {component, newState, composeActions, memoize} from 'omreact';
// ...
const update = (action, state, props) => action.match({
add: value =>
newState({value: state.value + value}),
addOnePlusTwo: () =>
composeEvents([events.add(1), events.add(2)], update, state, props),
});
// ...
$ cd examples
$ yarn install
$ yarn start
Check the examples directory in the repository.
FAQs
Purely functional React components with local state
We found that omreact 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.

Product
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.

Security News
ENISA has become a CVE Program Root, giving the EU a central authority for coordinating vulnerability reporting, disclosure, and cross-border response.

Product
Socket now scans OpenVSX extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.