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-finite-state-machine

v1.1.7

Published

A library for seamlessly creating and managing state machines in Redux-Saga

Downloads

182

Readme

Redux-Saga-Finite-State-Machine

Redux-Saga-Finite-State-Machine is a TypeScript library designed for efficient state machine management within Redux-Saga environments.

Features

  • Define and manage state machines using Redux-Saga.
  • Handle state transitions based on Redux actions.
  • Custom error and cancellation handling within state machine workflows.

Example project

https://github.com/abaikov/rsfsm-example

Related Packages

If you are using redux-saga-finite-state-machine, you might also find the following related packages useful:

  • react-redux-saga-finite-state-machine: An extension of Redux Saga Finite State Machine specifically tailored for React applications. It provides additional bindings to work seamlessly with React and Redux.

Installation

Install Redux-Saga-Finite-State-Machine using npm:

npm install redux-saga-finite-state-machine

Or using yarn:

yarn add redux-saga-finite-state-machine

Usage

Defining a State Machine

Here is a general implementation of a state machine, demonstrating how to handle user actions and system events:

import { RSFiniteStateMachine } from 'redux-saga-finite-state-machine';
import { take, put } from 'redux-saga/effects';

// Define your states and transitions
const myStateMachineProps = {
    defaultState: 'idle',
    stateSelector: (myReduxStoreState, runProps) => 
        myReduxStoreState.myModule.entities[runProps.id].state, // or 'status' 
    // It will block everything until finished
    onStart: function* (runProps) {
        //Here you can init subscriptions to sockets or any other events
        //and pass the saga channel to other components as 'startProps'
        const myChannel = eventChannel(emitter => {
            // Subscribe

            // The subscriber must return an unsubscribe function
            return () => {
                //Unsubscribe
            }
        });
        const channel = yield call(myChannel, value);

        return {
            channel
        }
    },
    states: {
        idle: function* (runProps, startProps) { 
            // Logic for idle state: waiting for user to enter the page
            yield take('USER_ENTERED_PAGE_ACTION');
            // or yield take(startProps.channel);
        },
        loading: function* (runProps, startProps) { 
            try {
                // Logic for loading state: simulate data fetching or processing
                yield put({ 
                    type: 'DATA_LOADING_COMPLETED_ACTION',
                    payload: {
                        id: runProps.id
                    }
                }); // Waiting for loading to complete
                // or startProps.emitter({
                //     type: 'DATA_LOADING_COMPLETED_ACTION',
                // })
            } catch (e) {
                yield put({
                    type: 'DATA_LOADING_FAILED_ACTION',
                    payload: {
                        id: runProps.id
                    }
                }); // Handling loading failure
            }
        },
        error: function* (runProps) { 
            // Logic for error state: wait for user to attempt to load again
            yield take('RETRY_LOADING_BUTTON_CLICK_ACTION');
        }
    },
    onStop: function* (runProps, startProps) {
        // Don't forget to unsubscribe
        startProps.channel.close();
    },
    handleError: function* (error, runProps) { console.error(error); }
};

// Instantiate the state machine with the defined properties
const myStateMachine = new RSFiniteStateMachine(myStateMachineProps);

Redux Toolkit Reducer Example

Here is how you can define a Redux reducer to manage the states of your state machine:

import { createReducer } from '@reduxjs/toolkit';

const initialState = {
    currentState: 'idle'
};

const appStateReducer = createReducer(initialState, (builder) => {
    builder
        .addCase('USER_ENTERED_PAGE_ACTION', (state) => {
            state.currentState = 'loading';
        })
        .addCase('DATA_LOADING_COMPLETED_ACTION', (state) => {
            state.currentState = 'success';
        })
        .addCase('DATA_LOADING_FAILED_ACTION', (state) => {
            state.currentState = 'error';
        })
        .addCase('RETRY_LOADING_BUTTON_CLICK_ACTION', (state) => {
            state.currentState = 'loading';
        });
});

Handling Actions with State Machines

If you do not need to run the state machine from a component but react to specific Redux actions, set up your sagas like this:

import { RSFiniteStateMachineEngine } from 'redux-saga-finite-state-machine'

const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
    reducer: ReduxStoreCombinedReducer,
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(sagaMiddleware),
});
const stateMachineEngine = new RSFiniteStateMachineEngine();

sagaMiddleware.run(function*() {
    yield all([
        takeEvery(
            START_MACHINE_ACTION_TYPE, 
            stateMachineEngine.runStateMachine(myStateMachine, {
                cancelSelector: STOP_MACHINE_ACTION_TYPE
            })
        )
    ]);
});

Usage with components

Setting Up the Middleware and Store

To integrate Redux-Saga-Finite-State-Machine with Redux and Redux-Saga in your application, set up your store with saga middleware as follows:

import { configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import { RSFiniteStateMachineEngine } from 'redux-saga-finite-state-machine';

const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
    reducer: rootReducer, // Your combined reducer
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(sagaMiddleware),
});

// Instantiate the state machine engine
const stateMachineEngine = new RSFiniteStateMachineEngine();

sagaMiddleware.run(function*() {
    yield fork(stateMachineEngine.start());
    // or yield all([ ...myOtherSags, call(stateMachineEngine.start()) ]);
});

Using the State Machine in a сomponent

If you are using React, you might consider using the React Redux Saga Finite State Machine package instead, which provides additional bindings specifically designed for React applications. However, here is a general approach to integrating the Redux Saga Finite State Machine in a React component:

function MyComponent(props) {
    React.useEffect(() => {
        const stop = stateMachineEngine.runMachineWithProps(myStateMachine, props);

        // Don't forget to stop the state machine
        return () => {
            stop();
        };
    }, [props]);

    return <div>Interactive Component with State Management</div>;
}

API Reference

IRSFiniteStateMachineProps

This interface describes the properties required to configure a finite state machine:

  • states: An object mapping each state to a generator function that executes the logic for that state.
  • defaultState: Optional. The initial state of the machine. Can be a literal value or a function that returns the state.
  • onStart: Optional. A blocking function called before the states generator functions.
  • onStop: Optional. A blocking function called in the end.
  • handleError: Optional. A function called when an error occurs within the state machine.

RSFiniteStateMachine

  • constructor(props: IRSFiniteStateMachineProps): Initializes a new state machine with the given properties.
  • createSaga(): Returns a saga that manages the state transitions based on the defined states.

RSFiniteStateMachineEngine

  • runStateMachine(stateMachine, options): Runs the specified state machine in response to dispatched actions. The options parameter can include:
    • cancelSelector: A selector or action type that triggers cancellation of the state machine.
    • mapActionToProps: A function that maps dispatched actions to props passed to the state machine.
  • start(): Starts listening for actions to trigger state transitions.
  • destroy(): Cleans up any listeners and ends any running sagas.

Contributing

Contributions are welcome!

License

Redux-Saga-Finite-State-Machine is MIT licensed. See the LICENSE file for more details.