2.0.2 • Published 5 years ago

react-imperator v2.0.2

Weekly downloads
6
License
MIT
Repository
github
Last release
5 years ago

react-imperator 2.0

This is a state container that automatically (more or less) injects updated properties into connected components. Starting with 2.0, the architecture is rewritten to be more helpful and automagic. Please note that 2.0 is not backwards compatible with 1.0, though it shouldn't be overly hard to convert.

The package exposes a single method:

CreateStore

function createStore<StoreState>(initialState: StoreState)

This method is responsible for creating the initial store based on the supplied value, and then exposing the following methods:

Subscribe

subscribe<T extends StoreProp>(context: T, callback: Callback<T>): string

subscribe is mainly used for services, a component would normally not depend on it. It takes the following arguments:

  • a type argument extending StoreProp (which in turn is an alias for keyof StoreState)
  • a context of type T
  • a callback accepting a parameter of type StoreStateT

The method returns a randomly generated name, which can be used to cancel a subscription

Unsubscribe

unsubscribe(name: string): void

unsubscribe is the reverse of subscribe, and removes all subscriptions of a specified name.

Get

get<T extends StoreProp>(context: T): StoreState[T]

get accepts a context of type T, and returns its current value. This is mainly used for components and services that don't necessarily want to subscribe to a specific context, but still might require its value

Update

update<T extends StoreProp>(context: T, reducer: (state: StoreState[T]) => StoreState[T]): void

update takes two arguments:

  • A context to update
  • A callback that receives the current state of the context, and is expected to return the new state

Connect

connect<LocalState>(Component: React.ComponentType<LocalState>, mapStoreToLocal: (state: StoreState) => LocalState): React.ComponentType<LocalState>

connect takes two arguments:

  • A component, may be stateless or stateful
  • A callback used to derive local state from the global state

Simple example

State.ts

import { createStore } from "react-imperator";

export interface IListState {
    items: IListItem[];
    text: string;
}

export interface IListItem {
    text: string;
    done: boolean;
    id: number;
}

export type StateMap<StoreState, LocalState> = (
    state: StoreState
) => LocalState;

export const { connect, get, update } = createStore<IListState>({
    items: [],
    text: ""
});

ListItems.tsx

import * as React from "react";
import { IListState, StateMap, connect, IListItem, update } from "./State";
import { AddItem } from "./AddItem";

interface IListItemsProps {
    list: IListItem[];
    text?: string;
}

const stateMap: StateMap<IListState, IListItemsProps> = (
    state: IListState
) => ({
    list: state.items || [],
    text: state.text
});

const handleToggle = (id: number) => (
    e: React.SyntheticEvent<HTMLInputElement>
) => {
    update("items", items =>
        items.map(item => {
            return item.id === id
                ? { ...item, ...{ done: e.currentTarget.checked } }
                : item;
        })
    );
};

export const ListItems = connect<IListItemsProps>(
    (props: IListItemsProps) => {
        return (
            <div>
                <ul>
                    {props.list.map(item => (
                        <li key={item.id}>
                            <input
                                type="checkbox"
                                checked={item.done}
                                onChange={handleToggle(item.id)}
                            />
                            {item.text}
                        </li>
                    ))}
                    <li>
                        <AddItem text="" />
                    </li>
                </ul>
            </div>
        );
    },
    stateMap
);

AddItem.tsx

import * as React from "react";
import { update, StateMap, IListState, connect } from "./State";

interface IAddItemProps {
    text: string;
}

const stateMap: StateMap<IListState, IAddItemProps> = (state: IListState) => ({
    text: state.text
});

export const AddItem : React.ComponentType<IAddItemProps> = connect(class extends React.Component<IAddItemProps, {}> {

    handleInput = (e: React.KeyboardEvent) => {
        if(e.which !== 13 || !this.props.text) {
            return;
        }

        this.handleAdd();
    }
    handleTextChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
        update("text", () => e.currentTarget.value);
    }

    handleAdd = () => {
        update("items", state => [...state, { text: this.props.text, done: false, id: state.length + 1 }]);
        update("text", () => "");
    }

    render(): JSX.Element {
        return (
            <div>
                <div>
                    <input value={this.props.text || ""} onChange={this.handleTextChange} onKeyDown={this.handleInput} />
                    <button onClick={this.handleAdd}>Add</button>
                </div>
            </div>
        );
    }
}, stateMap);

react-imperator 1.0

This is a state container that automatically (more or less) injects updated properties into connected components.

The package exposes two methods, connect and update.

Connect

connect: <S>(Component: React.ComponentType<S>, contexts?: string[], excludedContexts?: string[]) => React.ComponentType<S>;

connect() takes three arguments:

  • A component, may be stateless or stateful
  • A list of contexts to force subscription (optional)
    • This is useful when you want to declare a property as optional, yet still want to add a subscription
  • A list of contexts to ignore (optional)

and returns a wrapped component

Update

update: <T>(context: string, producer: (state: T) => T) => void;

update() takes two arguments:

  • A context to update
  • A callback that receives the current state of the context, and is expected to return the new state

Simple example

ShowCount.tsx - Subscribes to the context "count", explicitly specified

import * as React from "react";
import { connect } from "react-imperator";

export const ShowCount = connect((props: { count?: number}) => <div>{props.count || 0}</div>, ["count"]);

Incrementer.tsx - Will update the context "count"

import * as React from "react";
import { update } from "react-imperator";

const click = () => {
    update<number>("count", value => (value || 0) + 1)
}

export const Incrementer = () => <button onClick={click}>Click me</button>;

New in 1.0.11

Imperator now exposes two additional methods:

subscribe: function<T>(context: string, callback: (state: T) => void): string;
unsubscribe: function(name: string): void;

These are helper methods to allow service classes and similar to monitor context changes. subscribe() takes two arguments:

  • A context to monitor
  • A callback to invoke when context changes And returns a random identifier for a subscription

unsubscribe() takes a single argument:

  • The subscription name to revoke (output from subscribe method)
2.0.2

5 years ago

2.0.1

5 years ago

2.0.0

5 years ago

1.0.14

5 years ago

1.0.13

5 years ago

1.0.12

5 years ago

1.0.11

5 years ago

1.0.10

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago