Options
All
  • Public
  • Public/Protected
  • All
Menu

@rxreact/context

styled with prettier Greenkeeper badge Build Status Coveralls

Development Sponsored By:
Carbon Five

A project for dependency injecting Signal Graphs into components. Builds on top of @rxreact/core.

Typedocs for @rxreact/context

Installation

In your project:

npm install @rxreact/jest-helpers --save

or

yarn add @rxreact/jest-helpers

RxJS and Jest are peer dependencies and need to be installed separately.

Then import the library:

import { connect, createSignalGraphContext } from "@rxreact/context";

Basic Usage

This library is patterned after Redux, where you have a global store for data and business logic, and individual components can connect to that store, add their own local business logic, then inject the results into components for rendering.

Like with Redux, in testing you may wrap a tree of components with a Provider to inject mock versions of your global store.

You can see a full working sample application here.

Setup Global Store

To set up the rxreact/context store, you will need a few pieces: a global SignalGraph, a SignalGraphContext, and to add the SignalGraphProvider to your App.tsx.

// src/signals/signal-graph.ts
import { Subject } from "rxjs";
import { scan, startWith, shareReplay } from "rxjs/operators";

// For typescript, export a type for use by viewModelFactories
export type SignalGraph = ReturnType<typeof signalGraph>;

/** A function to generate the global signal graph */
export const signalGraph = () => {
  const onClick$ = new Subject<void>();

  // Set up your signal graph
  const clickCount$ = onClick$.pipe(
    scan(count => count + 1, 0),
    startWith(0),
    shareReplay(1)
  );

  // Return all signals you want to expose to components
  return {
    onClick$,
    clickCount$
  };
};
// src/signals/SignalGraphContext.ts
import { createSignalGraphContext } from "@rxreact/context";
import { signalGraph } from "./SignalGraph";

// Generate and export a context and provider for the graph
export const [SignalGraphContext, SignalGraphProvider] = createSignalGraphContext(signalGraph);
// src/App.tsx

import * as React from "react";
import { SignalGraphProvider } from "../signals/SignalGraphContext";
import ClickCounter from "./ClickCounter";

const App: React.FunctionComponent = () => {
  return (
    // Any component inside the provider can connect to the graph.
    <SignalGraphProvider>
      <ClickCounter multiple={3} />
    </SignalGraphProvider>
  );
};

export default App;

Connect to store with components

import * as React from "react";
import { Observable, combineLatest } from "rxjs";
import { map } from "rxjs/operators";
import { getProp, connect } from "@rxreact/context";

import { SignalGraph } from "../signals/SignalGraph";
import { SignalGraphContext } from "../signals/SignalGraphContext";

// Set up an interface for your normal props
export interface ClickCounterProps {
  multiple: number;
}

// Set up a different interface for props passed in from the viewModelFactory (this makes typing the viewModelFactory easier)
export interface ClickCounterSignalProps {
  clickCount: number;
  color: string;
  onClick: () => void;
}

// Your component takes both interfaces as props
export const ClickCounter: React.FunctionComponent<ClickCounterProps & ClickCounterSignalProps> = ({
  color,
  onClick,
  clickCount
}) => {
  return (
    <div>
      <button onClick={onClick}>Click Me</button>
      <p>
        Clicked <span style={{ color }}>{clickCount}</span> times
      </p>
    </div>
  );
};

// Create (and export for testing) a viewModelFactory that has the signal graph dependency injected into it.
export const viewModelFactory = (
  // Pull out individual signals to make it easy to see the dependencies of the component.
  { onClick$, clickCount$ }: SignalGraph,
  // The factory can also take an Observable of external props
  props$: Observable<ClickCounterProps>
) => {
  // Set up any local signals you want
  const multiple$ = props$.pipe(getProp("multiple"));
  const color$: Observable<string> = combineLatest(clickCount$, multiple$).pipe(
    map(([count, multiple$]) => count % multiple$ === 0),
    map(isMultiple => (isMultiple ? "red" : "black"))
  );

  // Inject signals into the component
  return {
    // Values
    inputs: {
      clickCount: clickCount$,
      color: color$
    },
    // Callbacks
    outputs: {
      onClick: onClick$
    }
  };
};

// Default export the connected component, passing in the SignalGraphContext so we know which graph to connect to.
export default connect(SignalGraphContext, viewModelFactory)(ClickCounter);

Index

Type aliases

ConnectViewModel

ConnectViewModel<Selectors, Actions>: ViewModel<Selectors, Actions> & { unsubscribe?: Subscription[] }

Type parameters

  • Selectors

  • Actions

NonSignalGraphProps

NonSignalGraphProps<Props, Selectors, Actions, OwnProps>: Difference<Props, Selectors & ActionMap<Actions>> & OwnProps

Type parameters

  • Props

  • Selectors

  • Actions

  • OwnProps

ObservableReducer

ObservableReducer<T, List>: [Observable<T>, (state: List, value: T) => List]

Type parameters

  • T

  • List

Result

Result<T>: OkResult<T> | ErrorResult

Type parameters

  • T

Functions

connect

  • connect<SignalGraph, Selectors, Actions, OwnProps>(context: Context<SignalGraph>, viewModelFactory: (signalGraph: SignalGraph, ownProps: Observable<OwnProps>) => ConnectViewModel<Selectors, Actions>): connectWithComponent
  • Type parameters

    • SignalGraph

    • Selectors

    • Actions

    • OwnProps

    Parameters

    • context: Context<SignalGraph>
    • viewModelFactory: (signalGraph: SignalGraph, ownProps: Observable<OwnProps>) => ConnectViewModel<Selectors, Actions>
        • (signalGraph: SignalGraph, ownProps: Observable<OwnProps>): ConnectViewModel<Selectors, Actions>
        • Parameters

          • signalGraph: SignalGraph
          • ownProps: Observable<OwnProps>

          Returns ConnectViewModel<Selectors, Actions>

    Returns connectWithComponent

createSignalGraphContext

  • createSignalGraphContext<T>(createSignalGraph: () => T): [Context<T>, FunctionComponent]
  • Generate a SignalGraph context and provider to serve connected components.

    Type parameters

    • T

    Parameters

    • createSignalGraph: () => T

      A function to generate the signal graph

        • (): T
        • Returns T

    Returns [Context<T>, FunctionComponent]

err

Const getAccessor

  • getAccessor<T>(externalValue$: Observable<T>): [Observable<T>, Subject<T>]
  • Type parameters

    • T

    Parameters

    • externalValue$: Observable<T>

    Returns [Observable<T>, Subject<T>]

getProp

  • getProp<Props, T>(prop: T): (Anonymous function)
  • An operator to extract a property from an observable of an object. Useful for pulling a specific prop from an observable of React props.

    Type parameters

    • Props

    • T: keyof Props

    Parameters

    • prop: T

      The name of the object property

    Returns (Anonymous function)

    • An observable of the object's property

isOk

ok

reducer

Const resolveObservable

  • resolveObservable<T>(o: Observable<T>): Promise<Result<T>>

Const resolveObservableOnValue

  • resolveObservableOnValue<T>(utils: MatcherUtils, o: Observable<T>, expected: T): Promise<Result<T>>
  • Type parameters

    • T

    Parameters

    • utils: MatcherUtils
    • o: Observable<T>
    • expected: T

    Returns Promise<Result<T>>

Const toEmit

  • toEmit<T>(this: MatcherUtils, o: Observable<T>): Promise<{ message: (Anonymous function); pass: boolean }>
  • Test that an observable did (or did not) emit anything

    Type parameters

    • T

    Parameters

    • this: MatcherUtils
    • o: Observable<T>

    Returns Promise<{ message: (Anonymous function); pass: boolean }>

Const toEmitValue

  • toEmitValue<T>(this: MatcherUtils, o: Observable<T>, expected: T): Promise<{ actual: undefined | T; message: () => string; pass: boolean }>
  • Test that an observable did (or did not) emit a specific value

    Type parameters

    • T

    Parameters

    • this: MatcherUtils
    • o: Observable<T>
    • expected: T

    Returns Promise<{ actual: undefined | T; message: () => string; pass: boolean }>

Const watchSignal

  • watchSignal<T>(o$: Observable<T>): Observable<T>
  • Subscribe to the given signal, so you can use expect(signal$).toEmit() on it later.

    Type parameters

    • T

    Parameters

    • o$: Observable<T>

      the observable to watch

    Returns Observable<T>

    a hot observable to test against

Legend

  • Module
  • Object literal
  • Variable
  • Function
  • Function with type parameter
  • Index signature
  • Type alias
  • Type alias with type parameter
  • Enumeration
  • Enumeration member
  • Property
  • Method
  • Interface
  • Interface with type parameter
  • Constructor
  • Property
  • Method
  • Index signature
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Accessor
  • Index signature
  • Inherited constructor
  • Inherited property
  • Inherited method
  • Inherited accessor
  • Protected property
  • Protected method
  • Protected accessor
  • Private property
  • Private method
  • Private accessor
  • Static property
  • Static method

Generated using TypeDoc