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

apollo-client-reduxify

v1.0.6

Published

A redux-like implementation of @apollo/client's reactive state management solution with advanced features.

Downloads

13

Readme

apollo-client-reduxify

A lightweight redux-like implementation of apollo-client's reactive state management solution.

If you're working on a project using both apollo-client 3+ and redux, then definitely you have a redundant state management. You may consider dropping redux (and react-redux, ...etc) and adopt the apollo-client's local state management solution using this package.

'Reactive Variable API' + 'Redux-like API' = 'apollo-client-reduxify'

apollo-client-reduxify does and doesn't have from redux:

  • redux is a state management solution while apollo-client-reduxify is a toolkit for a redux-like implementation of apollo-client's state management solution using reactive variables.

  • redux is a single large state tree while apollo-client-reduxify state is composed of many state trees. This allows react component to RE-RENDER ONLY based on the affected state changes.

  • just like redux, apollo-client-reduxify implements middleware, reducers, and selectors.

  • in redux, a case reducer has two parameters (previous state and action) while in apollo-client-reduxify, it has three parameters (previous state, action, and context). The context parameter provides useful API and metadata which enables case reducer to access other reactive variable state, dispatch an action, or even execute a function (provided in store creation).

  • apollo-client-reduxify provides a feature for watching the dispatched actions and the state changes. The developer has the option to watch only a particular state tree or all state trees.

  • redux requires too much boilerplate code while apollo-client-reduxify implements concise and strongly typed reducers and selectors syntax.

  • apollo-client-reduxify provides strongly typed API for dispatching an action and consuming a state (via selectors).

  • in apollo-client-reduxify, the developer has the option to persist a particular state while keeping other state not to persist. Unlike in redux, you have to persist the whole large state tree, if needed.

  • both have reactivity during a state change.

  • the cool thing about apollo-client-reduxify is that all of the created state trees are just reactive variables, this means that the developer can still use reactive variables API provided by apollo-client.

Installation

npm install apollo-client-reduxify

How to use?

Creating Reactive Variable Context (reactiveVars, reducers, selectors)

import { createReactiveVar } from 'apollo-client-reduxify';

// for './notifications.js'
const initialState = {
  isOpen: false,
  message: '',
};

export const notificationsVar = createReactiveVar({
  name: 'notificationsVar',
  initialState,
  reducers: {
    setNotificationShow: (state, payload) => ({
      ...state,
      isOpen: true,
      message: payload,
    }),
    setNotificationHide: () => initialState,
  },
  selectors: {
    getNotification: (s) => s,
    getNotificationMessage: (s, props) => {
      if (props.userId === 'me') {
        return null;
      } else {
        return s.message;
      }
    },
  },
});

// for './modals.js'
const initialState = {
  isOpen: false,
  promptMessage: '',
};

export const modalsVar = createReactiveVar({
  name: 'modalsVar',
  // you have the option to persist a particular state, while keeping others not to persist.
  persistTo: 'sessionStorage',
  initialState,
  reducers: {
    // you can access other reactive variable state or even dispatch an action via 'context' argument.
    // 'context' includes default API such as 'dispatch','reactiveVars', and 'nonHookSelectors'.
    // the developer can add metadata to this context during 'store' creation under context option
    setModalShow: (state, payload, context) => {
      const { reactiveVars, logger } = context;
      const notificationsState = reactiveVars.notificationsVar();

      // ... your code logic using 'notificationsState'

      logger("dispatched action: 'setModalShow'");

      return {
        ...state,
        isOpen: true,
        promptMessage: payload,
      };
    },
    setModalHide: () => initialState,
  },
  selectors: {
    getModal: (s) => s,
  },
});

Combining Reactive Variable Instances

// for './rootVars'

import { mergeReactiveVars } from 'apollo-client-reduxify';

import { notificationsVar } from './notifications';
import { modalsVar } from './modals';

export default mergeReactiveVars({
  notificationsVar,
  modalsVar,
});

Creating a store and accessing its API

// for './store'

import { createReactiveVarStore, applyMiddleware } from "apollo-client-reduxify";

import rootVars from "./rootVars";


const store = createReactiveVarStore(rootVars, {
  enableLog: true, // or ["modalsVar", ...etc] if you want to log a particular state only
  context: {
      // ... you can provide an additional context here to access by all reducers
      // for a logic or running a function purposes. We add a logger in this case.
      logger(value){
          console.log(value)
      }
  },
  middleware // you can add an optional middleware
});


const middleware = applyMiddleware(
  ({ reactiveVars, dispatch }) =>
    (action, next) => {
      console.log("this is middleware 1");
      if (action.type === "setModalShow") dispatch("otherAction...", {});
      next();
    }
  () => (action, next) => {
    next();
    console.log("this is middleware 2");
  }
);


export default store;

Connecting Reactive Variables to Apollo Client's Cache

import { InMemoryCache, ApolloClient } from '@apollo/client';
import store from './store';

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        ...store.fieldTypes, // < --- IMPORTANT!!!
        // ...other field policies
      },
    },
  },
});

const client = new ApolloClient({
  cache,
  // ...other options
});

Consuming a state and dispatching an action (for your react component)

There are flexible ways of accessing a state and dispatching an action:

  1. wrapping your react component using 'reactiveVarConnector' HOC (like react-redux's mapStateToProps, mapDispatchToProps method)
  2. use directly the dispatch, useReadReactiveVar, and readReactiveVar helper functions by the react component.
  3. via reactiveVars (see the documentation about "Reactive variables" for more information).
  4. via useQuery together with your graphql query (see the documentation about "Querying local state" for more information).

METHOD 1. Wrapping your react component using 'reactiveVarConnector' HOC

// for './App'

import store from './store';

// 'reactiveVarConnector' function provides typescript intellisense like react-redux's 'connect' api.
// This means that all of your pre-defined 'selectors' and 'actions' will be provided by typescript
// to your component.
const connect = store.reactiveVarConnector(
  (selectors, ownProps) => {
    return {
      notification: selectors.getNotification,
      message: selectors.getNotificationMessage(ownProps),
    };
  },
  (actions) => ({ setNotificationShow: actions.setNotificationShow }),
);

const App = ({ notification, message, setNotificationShow }) => {
  return (
    <div>
      <p>`Notification: ${notification.message}`</p>
      <p>`Message: ${message}`</p>
      <button onClick={() => setNotificationShow('Hello World!')}>Show Notification</button>;
    </div>
  );
};

export default connect(App);

OPTION 2. Use directly the dispatch, useReadReactiveVar, and readReactiveVar API

//
import store from './store';

const toastNotificationShow = (message) => {
  // ....your code logic

  // all of the parameters are strongly typed
  // the first parameter is the 'action' to dispatch and the other is the 'payload'
  store.dispatch('setNotificationShow', message);
};

const getNotification = () => {
  // ...your code logic

  // the parameter is the 'selector' name which is strongly typed.
  return store.readReactiveVar('getNotification');
};

// OR
const SampleComponent = () => {
  const message = store.useReadReactiveVar('getNotificationMessage', { userId: 'me' });

  return (
    <div>
      <p>`Message: ${message}`</p>
      <button onClick={() => store.dispatch('setNotificationShow', 'Hello World!')}>Show Notification</button>;
    </div>
  );
};

IMPORTANT NOTE: You can still use reactive variables' API provided by apollo-client

const SampleComponent = () => {
  const { notificationsVar } = store.reactiveVars;
  const notification = notificationsVar();

  return (
    <div>
      <p>`Message: ${notification.message}`</p>
      <button onClick={() => notificationsVar({ isOpen: true, message: 'Hello world!' })}>Show Notification</button>;
    </div>
  );
};

For typescript users, see EXAMPLES here.