
Security News
The Nightmare Before Deployment
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.
component-from-stream
Advanced tools
component-from-stream on steroids: create a React-like component from any React-compatible library, that sources its props from an observable stream and supports middleware.
create a React-like component from any React-compatible library, that sources its props from an observable stream.
based on component-from-stream from recompose, with the following enhancements:
React-compatible library,React-like Component class,
e.g. PREACT or Inferno.component-from-stream-redux.props dispatcher instead of the default dispatcher,component-from-stream-redux operator.componentWillUnmount.props object,compatible with observable libraries such as RxJS
or MOST
the stateless rendering function (the component's view) is separated
from the potentially stateful reactive operator (the component's behaviour)
which maps the component's input stream of props to that of its rendering function.
Separation of behaviour from view has many advantages, among which:
props = no DOM involved.see the full example in this directory.
run the example in your browser locally with npm run example
or online here.
this example demonstrates how to implement component-from-stream Components
described in terms of their view and composed behaviour.
for an example of a redux-like setup, see that in the
component-from-stream-redux
operator module.
component-from-stream.ts
import createComponentFromStreamFactory, {
ComponentFromStreamFactory,
ComponentFromStreamConstructor
Operator as GenericOperator,
DispatchOperator as GenericDispatchOperator
} from 'component-from-stream'
import { InfernoChildren, Component } from 'inferno'
import { from } from 'rxjs'
export {
ComponentFromStreamFactory,
ComponentFromStreamConstructor,
Component,
InfernoChildren
}
export type Operator<I={},O=I> = GenericOperator<I,O,Observable<I>,Observable<O>>
export type DispatchOperator<A=void,I={},O=I> =
GenericDispatchOperator<A,I,O,Observable<I>,Observable<O>>
// export a component-from-stream factory based on Inferno and RxJS
export default createComponentFromStreamFactory<Component<any,any>,InfernoChildren>(
Component,
from
)
// export helper to compose operators
export function compose <I,O>(...operators: Operator<any,any>[]): Operator<I,O> {
return function (q$: Observable<I>): Observable<O> {
return q$.pipe(...operators)
}
}
copy-button/index.ts
import Button from './view'
import copyButtonBehaviour from './behaviour'
import componentFromStream, {
ComponentFromStreamConstructor, Component, InfernoChildren, Operator
} from '../component-from-stream'
export default componentFromStream(Button, copyButtonBehaviour)
copy-button/behaviour.ts
import { ButtonViewProps } from './view'
import { compose } from '../component-from-stream'
import { shallowEqual, shallowMerge, pick, log } from '../utils'
import { into } from 'basic-cursors'
import withEventHandler from 'rx-with-event-handler'
import { Observable } from 'rxjs'
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'
export default compose<CopyButtonProps,ButtonViewProps>(
map(shallowMerge(DEFAULT_PROPS)), // icons are not deep-copied
tap(log('copy-button:props:')),
withEventHandler('click')(switchMap(doCopyToClipboard)),
withToggleDisabledOnSuccess,
tap(log('copy-button:toggle-disable-on-success:')),
pickDistinct('disabled', 'onClick', 'icons'), // clean-up
map(into('icon')(iconFromDisabled)),
distinctUntilChanged(shallowEqual), // only render when necessary
tap(log('copy-button:view-props:')),
)
function doCopyToClipboard
<P extends { event: E, value: string }, E extends { payload: MouseEvent }>(
props: P
): Observable<P & { success: true }> {
const { event, value } = props
event.payload.preventDefault()
return from(copyToClipboard(value)).pipe(
mapTo({ ...(<object>props), success: true } as P & { success: true }),
catchError(empty) // do nothing on error
)
}
function pickDistinct <P={}>(...keys: (keyof P)[]) {
return compose<P,Pick<P,keyof P>>(
map(pick(...keys)),
distinctUntilChanged(shallowEqual)
)
}
function iconFromDisabled ({ disabled, icons }: any) {
return disabled ? icons.disabled : icons.enabled
}
// ...
withEventHandler('click') from rx-with-event-handler
adds an onClick prop and emits the extended props object
whenever it receives a new input.
whenever the onClick handler is called (from the rendered Component),
it also adds an event prop to the emitted props object,
and pipes the latter through the given event handler operator,
in this case switchMap(doCopyToClipboard),
which copies the value prop to the clipboard
and adds a success prop set to true on success.
the resulting event can then be further processed by a downstream operator,
e.g. withToggleDisabledOnSuccess, which toggles the boolean disabled prop
for a brief moment whenever the success prop turns true.
check the code
for implementation details of this operator.
the component-from-stream factory is not directly exposed by this module.
instead, a higher-level factory is exposed for injecting the following dependencies:
Component from a React-like library,
e.g. PREACT or Inferno.RxJS
or MOST.this higher-level factory returns the required component factory
after injection of the supplied dependencies.
it is typically only required once in a project,
the resulting component-from-stream factory being exposed to the project's
other modules.
the above example illustrates the traditional instatiation of a component-from-stream,
with a single reactive operator that maps incoming props to view props.
the component factory may however be given additional optional arguments, for structuring more complex behaviours into self-documenting code:
onprops dispatcher factory: a factory that returns a custom props dispatcher,
e.g. to add default props, event handlers,
and/or to dispatch an FSA
object with props as payload....operators: rest arguments are operators
that may dispatch back into source stream.
see the component-from-stream-redux
middleware module for an example middleware implementation.import { Subscribable } from 'rx-subject'
export { Subscribable }
export default function createComponentFromStreamFactory<C extends Component<N, any, any>, N>(
ComponentCtor: new (props: any, context?: any) => C & Component<N, any, any>,
fromESObservable: <T, O extends Subscribable<T>>(stream: Subscribable<T>) => O,
opts?: Partial<ComponentFromStreamOptions>
): ComponentFromStreamFactory<C, N>
export default function createComponentFromStreamFactory<C extends Component<N, any, any>, N>(
ComponentCtor: new (props: any, context?: any) => C & Component<N, any, any>,
fromESObservable: <T, O extends Subscribable<T>>(stream: Subscribable<T>) => O,
toESObservable: <T, O extends Subscribable<T>>(stream: O) => Subscribable<T>,
opts?: Partial<ComponentFromStreamOptions>
): ComponentFromStreamFactory<C, N>
export interface ComponentFromStreamFactory<C extends Component<N, any, any>, N> {
<P = {}, Q = P>(
render: (props: Q) => N,
operator: Operator<P, A>
): ComponentFromStreamConstructor<C, N>
<P = {}, Q = P, A = P>(
render: (props: Q) => N,
onProps: DispatcherFactory<P, A>,
operator: DispatchOperator<A, A, any>,
...operators: DispatchOperator<A, any, any>[]
): ComponentFromStreamConstructor<C, N>
}
export interface ComponentFromStreamOptions {}
export interface ComponentFromStreamConstructor<C extends Component<N, any, any>, N> {
new <P = {}, Q = P>(props?: P, context?: any): C & ComponentFromStream<N, P, Q>
}
export interface ComponentFromStream<N, P = {}, Q = P>
extends Component<N, P, PropsState<Q>> {
componentWillMount(): void
componentWillReceiveProps(nextProps: Readonly<P>, nextContext: any): void
componentWillUnmount(): void
shouldComponentUpdate(props: Readonly<P>, state: Readonly<PropsState<Q>>): boolean
}
export interface ComponentConstructor<N> {
new <P = {}, S = {}>(props: P, context?: any): Component<N, P, S>
}
export interface Component<N, P = {}, S = {}> {
setState(state: Reducer<S, P> | Partial<S>, cb?: () => void): void
render(props?: P, state?: S, context?: any): N | void
props: Readonly<P>
state: Readonly<S | null>
context: any
}
export interface PropsState<Q> {
props: Q
}
export declare type DispatcherFactory<P, A = P> =
<S extends Subscribable<A>>(dispatch: (v: A) => void) => (props: P) => void
export type Operator<
I={},
O=I,
U extends Subscribable<I> = Subscribable<I>,
V extends Subscribable<O> = Subscribable<O>
> = (source$: U) => V
export type DispatchOperator<
A=void,
I={},
O=I,
Q extends Subscribable<I> = Subscribable<I>,
S extends Subscribable<O> = Subscribable<O>
> = (
source$: Q,
dispatch?: StreamableDispatcher<A>,
fromESObservable?: <T, O extends Subscribable<T>>(stream: Subscribable<T>) => O,
toESObservable?: <T, O extends Subscribable<T>>(stream: O) => Subscribable<T>
) => S
export interface StreamableDispatcher<A, S extends Subscribable<A> = Subscribable<A>> {
next(val: A): void
from<E extends Subscribable<A>>(source$: E): void
source$: S
}
export declare type Reducer<A, V> = (acc: A, val: V) => A
export declare function identity<T>(v: T): T
Symbol.observableThis module expects Symbol.observable to be defined in the global scope.
Use a polyfill such as symbol-observable
and if necessary a Symbol polyfill.
Check the symbol-observable-polyfill script
for an example of how to generate the standalone polyfill,
which can than be loaded from a script tag,
or simply add import 'symbol-observable' at the top of your project's main file.
although this library is written in TypeScript, it may also be imported into plain JavaScript code: modern code editors will still benefit from the available type definition, e.g. for helpful code completion.
Copyright 2018 Stéphane M. Catala
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and Limitations under the License.
FAQs
lightweight (1kb gz) component-from-stream on steroids: create a React-like component from any React-compatible library, that sources its props from an observable stream and supports middleware.
The npm package component-from-stream receives a total of 4 weekly downloads. As such, component-from-stream popularity was classified as not popular.
We found that component-from-stream 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
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.

Research
/Security News
Impostor NuGet package Tracer.Fody.NLog typosquats Tracer.Fody and its author, using homoglyph tricks, and exfiltrates Stratis wallet JSON/passwords to a Russian IP address.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.