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

redux-saga-react-engine

v1.0.0

Published

a redux-saga engine oriented framework

Downloads

4

Readme

redux-saga-react-engine

a redux-saga engine oriented framework

What is redux-saga-react-engine ?

redux-saga-react-engine is a framework that let you integrate the redux-saga redux side-effect manager into your react Components like a breaze

Why redux-saga-react-engine ?

this library intent to address a modular approach to the redux-saga side effect management, and get ride of some caveats:

  • avoid using redux actions to trigger side effect
  • keep saga logic organised and isolated
  • simplify redux-saga initialisation

Contents

  1. Providing saga into views
  2. Define and expose the logic
  3. Take advantage of react Hooks
  4. Wiring it all up
  5. Extend the experience

providing saga into views

redux-saga-react-engine embrasse the redux approach of injecting functionnalities into React components. a connector and a hook-oriented approaches are provided:

connector way :

// component/GreetUser.ts
import connectEngine from "redux-saga-react-engine";

function GreetUser({ fetchUserData, userData, userId }) {
  useEffect(() => {
    fetchUserData(userId);
  }, []);

  return <div>Hello, {userData.name} !</div>;
}

const mapUserSagaTopProps = (exec) => ({
  fetchUserData: (userId) => {
    exec("fetchUserData", userId);
  },
});

export default connectEngine([
  [
    "userSaga",
    [startAdapter((props) => ({ startArg: props.userId }))],
    mapUserSagaTopProps,
  ],
])(GreetUser);

where userData is retrieved from the redux store

hook way :

// component/GreetUser.ts
import { useSaga, useEngineAdapter } from "redux-saga-react-engine/hooks";

function GreetUser(props) {

    const  = { userData, userId } = props;

  const engine = useSaga('userSaga');
  const fetchUserData = userId => {
      engine('fetchUserData', userId);
  };

  useEngineAdapter(props, [useStartAdapter(props => ({startArg: props.userId}))])

  useEffect(() => {
    fetchUserData(userId);
  }, []);

  return <div>Hello, {userData.name} !</div>;
}

export default GreetUser;

bot methods give access to an engine, and it's exposed functionnalities, accessibles with their function names.

define and expose the logic

In order to access a saga engine, you have to register it with the helper function provided by the framework :

// saga/user-saga.ts
import { register } from "redux-saga-react-engine/helpers";
import {
  putEngineChannel,
  takeEveryEngineChannel,
  takeEngineChannel,
} from "redux-saga-react-engine/effects";
import { call, put } from "redux-saga/effects";
import StartAdapter from "./adapters";

register(
  "userSaga", // engineName
  true, // is a root engine (if true, should provide a bootstrapGenerator saga function in coreLogic definition)
  {
    *fetchUserDataSaga({ userId }) {
      const userData = yield call(/*network call*/);
      yield call(this.storeUserDataSaga, userData);
    },
    *storeUserDataSaga(userData) {
      yield put(/* event to store data in redux store */);
    },
    *watch() {
      // logic watcher
      yield takeEngineChannel("APP_STARTED");
      yield takeEngineChannel("INIT");
      yield takeEveryEngineChannel("FETCH_USER_DATA", this.fetchUserDataSaga);
      yield takeEveryEngineChannel("FETCH_USER_DATA", this.fetchUserDataSaga);
    },
  },
  {
    *bootstrapGenerator() {
      yield putEngineChannel("userSaga", { type: "APP_STARTED" });
    },
    *watcherGenerator() {
      // mandatory, the engine initialisation
      yield putEngineChannel("userSaga", { type: "INIT" });
    },
    *start(startArgs) {
      yield putEngineChannel("userSaga", {
        type: "POST_USER_ACCESS",
        userId: startArgs,
      });
    },
    *fetchUserData(userId) {
      yield putEngineChannel("userSaga", { type: "FETCH_USER_DATA", userId });
    },
  },
  [StartAdapter]
);

with this simple example, few remarks arise :

  • engines logic is encapsulated : the engine logic is defined in an object, an cannot access other engines internal logic. this promotes SOC and helps organise application domaines logic.
  • each engine define a public api, wich expose the public actions Components are allowed to access and execute. this helps keeping the code clearer
  • each engines is defined a unique channel, wich serve as a bus for dispatching action internally and from other engines. this way, one knows where an information is dispatched.

take advantage of react Hooks

redux-saga-react-engine provide a simple way to generalise engine functionnality across mutliple components with adapters : in the previous exemples, we used a few extra functionnalities of the framework :

  • a connector adapter
// ./engines/userSaga.js
connectEngine([
  [
    "userSaga",
    [startAdapter((props) => ({ startArg: props.arg }))],
    mapUserSagaTopProps,
  ],
])(GreetUser);
  • a hook adapter :
useEngineAdapter(props, [
  useStartAdapter((props) => ({ startArg: props.userId })),
]);
  • an adapter Validator:
import StartAdapter from "./adapters";

register(
  /* ... */
  [StartAdapter]
);

the validator take the above form, and ensure the engine ability to match the adapter requirement :

// ./adapter/index.js
const StartStopAdapter = (superclass) =>
  class extends superclass {
    constructor(args) {
      super(args);
      this.registerHandlers(["start"], args);
    }
  };

export default StartStopAdapter;

the engine manager registerHandlers check the functionnality definition at registration

the adapters is a custom hook, wich is given the engine manager and an engine name :

// ./adapter/index.js
import { useEffect, useCallback } from "react";

const startAdapter = (manager, managerKey, { ...props }) => {
  const { startArgs } = { startArgs: [], ...props };
  useEffect(
    useCallback(() => {
      manager.exec(managerKey, "start", ...startArgs);
    }, [manager, managerKey, startArgs]),
    []
  );
};

to use it as a connector Adapter, simply pass it to the adapter factory function :

// ./adapter/index.js
import { adapterFactory } from "redux-saga-react-engine/adapters";
/* ... */
export default adapterFactory(startAdapter);

and as a hook, through the hook Factory :

// ./adapter/index.js
import { hookFactory } from "redux-saga-react-engine/adapters";
/* ... */
export default hookFactory(startAdapter);

the factory allow you to pass a function tha maps the component props to the arguments required for the adapter.

With this in place, your able to generalise behaviour through your application, without the overhead of managing the definition in every components In the previous exemple, we defined a behaviour wich would be triggered on the first rendering of the component, but you can leverage any behaviour allowed by React hook API.

wiring it all up

so far, we defined our logic, integrated it in Components, and defined a bit of reusable behaviour, but all doest it come all together ? redux-saga-react-engine bring to you a simple and solution to initialise your application :

// ./index.js
// initialise logic registration
import "./engines";
import createSagaMiddleware from "redux-saga";
import LogicEngineManager from "redux-saga-react-engine/lib/logic-engine";

const sagaMiddleware = createSagaMiddleware();
/* other middleware configuration */
logicEngineManager.initSagas(sagaMiddleware);
LogicEngineManager.bootstrap(args);

first, you import your engine definitions, then initialize them by calling initSaga(sagaMiddleware) and finally call bootstrap with any startup arguments required the bootstrap function will recursively call every bootstrapGenerator of your engines is they are tagged as isRoot=true

extend the experience

redux-saga-react-engine allow you to extends the engine with extensions, that you can register using :

registerExtensionBefore(
   fn: (engineManager: LogicEngineManager, ...args: unknown[]) => unknown,
   engineManager: LogicEngineManager,
   ...args: unknown[]
 )

and

registerExtensionAfter(
   fn: (engineManager: LogicEngineManager, ...args: unknown[]) => unknown,
   engineManager: LogicEngineManager,
   ...args: unknown[]
 )

simply pass a function bootstraping your extension, and it will be loaded up, before engine initialisation for the first one, after for the second one

note: in order to be taken into account, the file registering your extensions should be loaded before the bootstrap call

Available extensions :

  • redux-saga-engine-network: a simple network extension, allowing you to register api and socket endpoints and handling them with saga effects