Socket
Socket
Sign inDemoInstall

@redux-cbd/context

Package Overview
Dependencies
0
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    @redux-cbd/context

Declarative annotations based context for redux.


Version published
Maintainers
1
Created

Readme

Source

🗻 @redux-cbd/context

npm version language-ts
start with wiki license
npm downloads HitCount


@redux/cbd context package that allow you to write much shorter reactive storages instead of redux.


Installation

For current ongoing package:

  • npm install --save @redux-cbd/context

Important:

  • Package uses 'expirementalDecorators' features (disabled by default for TypeScript transpiler).

What is inside

@AnnotationsUtils
@ConsumeReactContextManager
@Provide-

Example (wiki contains more explanations):

Application entrypoint.

import * as React from "react";
import {render} from "react-dom";

import {EntryPoint} from "@redux-cbd/utils";
import {MainView, IMainViewExternalProps} from "./view/MainView";

@EntryPoint()
export class Application {

  /*
   * { ...{} as IConnectedComponentExternalProps } is the trick for correct types handling.
   * Actually, connected component is different from the one we exported with 'export class'.
   * We should use default export with separate props cast or make such mock trick.
   * (I prefer second style with single class declaration and DIRECTLY NAMED imports, which are better).
   */
  public static main(): void {
    render(<div>
      <MainView someLabelFromExternalProps={ "First component." } { ...{} as IMainViewExternalProps }/>
      <MainView someLabelFromExternalProps={ "Second component." } { ...{} as IMainViewExternalProps }/>
    </div>, document.getElementById("application-root"));
  }

}

Context store reexport and signleton creation.

import {AuthContextManager, IAuthContext} from "./AuthContextManager";

export const authContextManager: AuthContextManager = new AuthContextManager();
export {AuthContextManager, IAuthContext} from "./AuthContextManager";

Context and handlers declaration.

import {Bind} from "@redux-cbd/utils";
import {ReactContextManager} from "@redux-cbd/context";

export interface IAuthContext {
  authActions: {
    setUser: (user: string) => void;
    setUserAsync: () => Promise<void>;
    changeAuthenticationStatus: () => void;
  };
  authState: {
    isAuthenticated: boolean;
    user: string;
  };
}

export class AuthContextManager extends ReactContextManager<IAuthContext> {

  private static ASYNC_USER_CHANGE_DELAY: number = 3000;

  // Default context state.
  protected readonly context: IAuthContext = {
    // Some kind of handlers.
    authActions: {
      changeAuthenticationStatus: this.changeAuthenticationStatus,
      setUserAsync: this.setUserAsync,
      setUser: this.setUser
    },
    // Provided storage.
    authState: {
      isAuthenticated: true,
      user: "anonymous"
    }
  };

  @Bind()
  public changeAuthenticationStatus(): void {
    this.context.authState.isAuthenticated = !this.context.authState.isAuthenticated;
    this.update();
  }

  @Bind()
  public setUser(user: string): void {
    this.context.authState.user = user;
    this.update();
  }

  @Bind()
  public setUserAsync(): Promise<void> {
    return new Promise((resolve) => {
      setTimeout(() => {
        this.context.authState.user = "user-" + Math.floor(Math.random() * 10000);
        this.update();

        resolve();
      }, AuthContextManager.ASYNC_USER_CHANGE_DELAY)
    });
  }

}

Connected component.

import * as React from "react";
import {PureComponent} from "react";
import {Consume, Provide} from "@redux-cbd/context";

// Store related things.

import {authContextManager, IAuthContext} from "../data";

// Props typing.

export interface IMainViewOwnProps { someLabelFromExternalProps: string; }

export interface IMainViewExternalProps extends IAuthContext {}

export interface IMainViewProps extends IMainViewExternalProps, IMainViewOwnProps {}

// Component related.

@Provide(authContextManager)
@Consume(authContextManager)
export class MainView extends PureComponent<IMainViewProps> {

  public render(): JSX.Element {
    const {
      someLabelFromExternalProps,
      authState: {user, isAuthenticated},
      authActions: {setUser, setUserAsync, changeAuthenticationStatus}
    } = this.props;

    const paddingStyle = { padding: "10px" };

    return (
      <div style={paddingStyle}>

        <div> External prop value: '{ someLabelFromExternalProps }' </div>

        <div style={paddingStyle}>
          <span>USERNAME: </span> {user} <br/>
          <span>AUTHENTICATED: </span>  {isAuthenticated.toString()} <br/>
        </div>

        <div style={paddingStyle}>
          <button onClick={changeAuthenticationStatus}>Change Authentication Status</button>
          <button onClick={setUserAsync}>Randomize User Async</button>
          <button onClick={() => setUser("user-" + Math.floor(Math.random() * 100))}>Randomize User</button>
        </div>

      </div>
    );
  }

}

Example build config.

import * as webpack from "webpack";
import * as path from "path";

const HtmlWebpackPlugin =  require("html-webpack-plugin");

const mode = process.env.NODE_ENV;
const projectRoot = path.resolve(__dirname, "./");

// For development purposes only.
// Extend and rewrite it properly with webpack documentation.
// Use proper config for production builds.
export class WebpackConfig implements webpack.Configuration {

  mode: "development" = "development";

  resolve = {
    extensions: [".ts", ".tsx", ".js", ".jsx"]
  };

  entry = [
    path.resolve(projectRoot, "src/Application.tsx")
  ];

  output = {
    path: path.resolve(projectRoot, "target/"),
    filename: "js/[name].bundle.js",
    sourceMapFilename: "js/map/[name].bundle.map"
  };

  devtool: "source-map" = "source-map";

  // Add the loader for .ts files.
  module = {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        loader: "awesome-typescript-loader",
        query: {
          configFileName: path.resolve(projectRoot, "./tsconfig.json")
        }
      }
    ]
  };

  plugins = [
    new HtmlWebpackPlugin({
      inject: true,
      filename: "index.html",
      template: path.resolve(projectRoot, "src/index.html")
    })
  ];

  devServer = {
    contentBase: "target/",
    historyApiFallback: true,
    compress: true,
    port: 3000,
    host: "0.0.0.0"
  }

}

export default new WebpackConfig();

Pure JS example:

import * as React from "react";
import {PureComponent} from "react";
import {render} from "react-dom";

import {Consume, Provide, ReactContextManager} from "@redux-cbd/context";

// Data store.

export class AuthContext extends ReactContextManager {

  changeAuthenticationStatus = () => {
    this.state.authState.isAuthenticated = !this.state.authState.isAuthenticated;
    this.update();
  };

  setUser = (user) => {
    this.state.authState.user = user;
    this.update();
  };

  setUserAsync = () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        this.state.authState.user = "user-" + Math.floor(Math.random() * 10000);
        this.update();
        resolve();
      }, 3000)
    });
  };

  // Wrap your actions and state separately to avoid naming collisions.
  context = {
    authActions: {
      changeAuthenticationStatus: this.changeAuthenticationStatus,
      setUserAsync: this.setUserAsync,
      setUser: this.setUser
    },
    authState: {
      isAuthenticated: true,
      user: "anonymous"
    }
  };

}

const authContext = new AuthContext();

// View.

/*
 * Single provide-consume component.
 * Actually, only one module component (for example, router) should provide context.
 * All you need - inject props by consuming.
 */
@Provide(authContext)
@Consume(authContext)
export class MainView extends PureComponent {

  render() {
    const {
      label,
      authState: {user, isAuthenticated},
      authActions: {setUser, setUserAsync, changeAuthenticationStatus}
    } = this.props;

    const paddingStyle = { padding: "10px" };

    return (
      <div style={paddingStyle}>

        <div> External prop value: '{ label }' </div>

        <div style={paddingStyle}>
          <span>USERNAME: </span> {user} <br/>
          <span>AUTHENTICATED: </span>  {isAuthenticated.toString()} <br/>
        </div>

        <div style={paddingStyle}>
          <button onClick={changeAuthenticationStatus}>Change Authentication Status</button>
          <button onClick={setUserAsync}>Randomize User Async</button>
          <button onClick={() => setUser("user-" + Math.floor(Math.random() * 100))}>Randomize User</button>
        </div>

      </div>
    );
  }

}

// Render into DOM.

render(
  <div>
    <MainView label={ "First component." }/> 
    <MainView label={ "Second component." }/>
  </div>,
  document.getElementById("application-root")
);

Documentation:

Repository wiki includes docs and samples.

Proposals and contribution:

Feel free to contibute or mail me with questions/proposals/issues (Neloreck@gmail.com).

Full examples

Repository includes example project with commentaries: link.
My own 'redux-cbd' based project: link.
Library unit tests also include some different examples of cbd usage.

Licence

MIT

Keywords

FAQs

Last updated on 11 Feb 2019

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc