npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@creatdevsolutions/cs-react-global-stores

v0.1.2

Published

Generic Store Provider to attach data to React Components using the react context api

Downloads

4

Readme

Global-Stores - A lightweight, dynamic React Context

Introduction

With this library, we want to provide a lightweigt, dynamic global store using the react context api. The idea is to combine partial stores into one global store and pass the global store (or only parts of it) down to react components using decorators.

Store definition

Start with the definition of your partial stores. A partial store should store any variables that belong to the same topic. Partial Stores can be implemented via js objects or in the more common way via es6 classes.

Implementing Stores via js objects

Partially stores are defined as interfaces for the types and a js object defining the default value. These partial stores have to extend the interface PartialStore. Additionally, you have to create a default value for this store.

interface UserStore extends PartialStore {
    userName: string;
}

const userStore: UserStore = {
    userName: "InitialValue",        
};

After defining all your partial stores, create one global store interface, which has all partial stores as members. This interface has to be exported to use it for the attached components, see later.

export interface MyStore extends Store {
    userStore: UserStore;
}

const store: MyStore = {
    userStore: userStore
};

Implementing Stores via es6 classes

To improve the code qualitiy and the implementation of stores, we provide the possibility to implement a partial store via es6 classes. To do so, just extend the abstract class PartialStoreClass.

export class UserStore extends PartialStoreClass {
    userName?: string;
}

To fill up your global store with a es6 class partial store, just call the constructor of your class to create a new instance of your partial store.

export interface MyStore extends Store {
    userStore: UserStore;
}

const store: MyStore = {
    userStore: new UserStore()
};

Providing the Store

If you finished defining all your partial stores and your global store, simply put it into the StoreProvider component.

class MyComponent extends React.Component {
    render() {
        return (
            <StoreProvider value={store}>
                <SampleComponent/>
                <SampleComponent2/>
            </StoreProvider>
        );
    }
}

Attach Components to Store

We provide 2 ways to attach the global store to react components. You can fully attach the global store to your component and it will receive the whole store object as prop or you can partially attach it to your component and device manually which parts of the store you want to have (analogue to react redux).

Important is the generic argument to our decorators. You have to pass the type of the props that your Component should receive in the tree (not injected by the global store). Consequently, we divided the Props of our Components into the Props that it gets from outside (Props) and the injected store props (StoreProps).

Fully Attached Components

To fully attach a component to your global store, simply decorate it with the AttachStore decorator.

import * as React from "react";
import {MyStore} from "./StoreTest";
import {AttachStore} from "..";

interface Props {
  test: string; // Obviously, any other properties are supported
}

interface StoreProps {
      store: MyStore;
}

class SampleComponent extends React.Component<Props & StoreProps> {

    render() {
        return (
          <p>
            {this.props.store.userStore.userName}
          </p>
        );
    }
}

// Calling the AttachStore decorator and your component will receive the
// global store
//
// By using the generic argument, only the prop test has to be passed
// to SampleComponent
export default AttachStore<Props>(SampleComponent);

Partially Attached Components

If you want to have only parts of your global store available in your component, you can decorate it with the AttachPartsOfStore decorator and pass a function into it, which maps the store down to your props. This is quite similar to react redux, but is easier to use with typescript.

import * as React from "react";
import {PartialStore} from "..";
import {MyStore} from "./StoreTest";
import {AttachPartsOfStore} from "../Provider/Provider";

interface Props {

}

interface StoreProps {
  userName: string;
}

class SampleComponent2 extends React.Component<Props & StoreProps> {

  render() {
        return (
          <button>
            {this.props.userName}
          </button>
        );
    }
}

// In this case, only the username is required
const mapStoreToProps: any  = (store: MyStore) => {
  return {
    userName: store.userStore.userName
  };
};

export default AttachPartsOfStore<Props>(mapStoreToProps)(SampleComponent2);

Update Global Store

Updating can also be done in 2 ways, regarding the attachment of your components. You can either call "setState" on a partial store in a fully attached component, or map the setState function to a prop in a partially attached component.

Fully Attached Components

Since each PartialStore provides a setState Method, you can simply call it on the partial store and it will trigger the state update in the global store.

import * as React from "react";
import {MyStore} from "./StoreTest";
import {AttachStore} from "..";

interface Props {
  test: string; // Obviously, any other properties are supported
}

interface StoreProps {
      store: MyStore;
}

class SampleComponent extends React.Component<Props & StoreProps> {

    render() {
        return (
          <button
            onClick={() =>
                this.props.store.userStore.setState({userName: "NewUser"})}
          >
            {this.props.store.userStore.userName}
          </button>
        );
    }
}

export default AttachStore<Props>(SampleComponent);

Partially Attached Components

The second way is to map the setState function of a store to a new prop, which is beneficial if not the whole partial store is required in a component. We recomment highly to use the following type for mapped setState function: "(store: Partial) => void;", since other types may result in typescript errors

import * as React from "react";
import {PartialStore} from "..";
import {MyStore} from "./StoreTest";
import {AttachPartsOfStore} from "../Provider/Provider";


interface Props {

}

interface StoreProps {
  userName: string;
  setUserStoreState: (store: Partial<PartialStore>) => void;
}

class SampleComponent2 extends React.Component<Props & StoreProps> {

  render() {
      console.log("render Sample Component");
        return (
          <button
            onClick={() =>
                this.props.setUserStoreState({userName: "NewUser"})}
          >
            {this.props.userName}
          </button>
        );
    }
}

const mapStoreToProps: any  = (store: MyStore) => {
  return {
    userName: store.userStore.test,
    setUserStoreState: store.userStore.setState
  };
};

export default AttachPartsOfStore<Props>(mapStoreToProps)(SampleComponent2);

Custom Functions in Partial Stores

We define two signatures for custom store functions. You can either have a function that returns parts of your partial store that will be merged into it or return a promise and change the store internally. For the second approach, implementation via es6 classes is required.

Update store via return value

We allow you to define custom functions on your partial stores to combine complex logic with store updates. The return value of the function will be merged into the partial store after the function is executed. This should be our alternative solution to actions and reducers of redux.

The signature for a custom function that updates the store with the return value looks like this:

func: (any) => Partial<PartialStore> // Insert your partial store type here

Example:

export interface UserStore extends PartialStore {
    login: (userName: string, password: string) => Partial<UserStore>;
    clientUser?: object;
}


const login: (userName: string, password: string) => Partial<UserStore> = (userName: string, password: string) =>  {
    
    // Do login here...                                    
   return {
        clientUser: {
             userName,
             password
        }
    };
};

const userStore: UserStore = {
    login: login,
};

Update store via setState

Partial stores implemented via es6 classes can change the state in custom functions simply calling this.setState. This is analogue to calling dispatch in redux, but we do not support changing the state of another partial store.

In the following example, we implemented a login method that returns a promise and internally updates the userStore and also waits until the update is finished before resolving:


export class UserStore extends PartialStoreClass {
    clientUser?: UserType;

    login(user: IUser, password: string): Promise<UserType> {

        return new Promise ((resolve, reject) => {
            service.login(user, password)
                .then((result: any) => {
                    
                    logger.info("User logged in");
                    this.setState({
                       clientUser: result.user
                    },
                    // Passing a callback as second parameter to setState
                    // To wait until state update finished
                    () => {
                        resolve(clientUser)
                    });
                })
                .catch((error: any[]) => {
                    logger.warn('User session closed.', error);
                    reject(error);
            });
        });
    }
}