0.0.3 • Published 6 years ago

redux-workflows v0.0.3

Weekly downloads
1
License
MIT
Repository
-
Last release
6 years ago

redux-workflows

A library for defining complex workflows spanning multiple screens, without relying on routes or complex state management. Whilst this library can be used for multiple page forms, it is agnostic as to its contents so is suitable for a wide variety of workflows

NPM JavaScript Style Guide

Install

npm install --save redux-workflows

Usage

Concepts

Many Workflow models rely on a single definition to control all of their interactions, entry and exit points. This quickly gets messy and difficult to modify. redux-workflows tries another approach by delegating this control to its components. This allows for much more flexibility at the cost of not having a single point of reference for the whole flow. You should examine the use case for your Workflow carefully and determine whether this approach is appropriate.

redux-workflows relies on a few key concepts to allow users to quickly define their flows:

Workflow

A Workflow is the top level element of redux-workflows, and contains a series of Stages. It must be specified with a unique name and an initial stage that corresponds to one of its child stages.

The Workflow lifecycle is as follows:

  • Initialisation - Happens when the Workflow is mounted and creates an entry in the Redux store for this workflow. If a previous workflow entry exists for this workflow name, it is overwritten.
  • Render - The component corresponding to the Workflow's initial stage is rendered.
  • Data Manipulation - The stage can programmatically push data to, or pull data from the Redux store, allowing data to be persisted between screens (or reloaded when the stage is entered again). There are deliberately very few restrictions on the shape of the data to allow maximum flexibility. Data is keyed by stage to make retrieval easier
  • Transitions - Stages can request a transition to any other stage in the workflow at any time. This will cause the current stage to unmount and the new stage to render. Any data not persisted will be lost
  • Completion - Any stage can signal completion of the Workflow (allowing for multiple exit points). Once the Workflow is completed, the onComplete callback that can be specified in the Workflow definition is invoked and passed all of the data persisted during the Workflow's lifecycle. Finally, the workflow state is removed from the Redux store

Stage

A Stage is a child element of the Workflow. It is specified with a unique (within the Workflow) name and a component to render. This component can be a pure React class/function or a higher-order or connected component. Any additional props specified on a stage will be passed down to its rendered component

There must be at least one, initial, stage in a Workflow and there is no upper limit. Stages can be specified in any order.

Flow Accessor

The Workflow mechanics are exposed to each rendered component via a prop named flow. The component can, at any point, invoke any of the Flow Accessor's functions to control the state of the Workflow. For example, the component can:

  • Save/Load data for any Stage (defaulted to the current stage)
  • Transition to another stage
  • Subscribe/Unsubscribe to Asynchronous events
  • Determine if an Asynchronous event has completed
  • Complete the Workflow

The flexibility of this approach makes it trivial to implement multiple entry/exit points to a workflow and easily step forwards/backwards through a user journey.

Simple Example

Below is a simple example of rendering and transitioning between two stages. For a more comprehensive and executable example, see the example folder

import React, {Component} from 'react'
import {Stage, Workflow} from 'redux-workflows'

const FirstStage = ({flow}) => (
  <button onClick={flow.transitionTo('s2')}>Go to step 2</button>
)

const SecondStage = ({flow}) => (
  <button onClick={flow.complete()}>Complete the Workflow</button>
)

class App extends Component {
  render() {
    const onComplete = () => console.log("Workflow Complete!")
    return (
      <Workflow name='test-flow' initialStage='s1' onComplete={onComplete}>
        <Stage name='s1' component={FirstStage}/>
        <Stage name='s2' component={SecondStage}/>
      </Workflow>
    )
  }
}

Asynchronous Events

It is often desirable to transition based on the outcome of an Asynchronous event (e.g. a success/failure of a http request). redux-workflows provides a standard pattern for doing this via the Flow Accessor:

  1. Subscribe to the action that indicates your Async event has completed
  2. Start your Async event
  3. Implement a check in your componentDidUpdate() method to determine when this action has occurred
  4. Unsubscribe from this action

Note: A workflow will always be notified that an action has occured after it has been fully processed by middleware and reducers

Example

In the below code, we will request a token asynchronously. We will wait for a REQUEST_TOKEN_COMPLETE action before transitioning to the next stage of the workflow. For brevity, the actual workflow definition is omitted, but a full async example can be found in the example folder.

import React, {Component} from 'react'
class TokenRequestPage extends Component{
  
  componentDidUpdate() {
    const {flow} = this.props

    if (flow.isActionObserved('REQUEST_TOKEN_COMPLETE')) {
      flow.unsubscribe('REQUEST_TOKEN_COMPLETE')
      flow.transitionTo('afterTokenRequestStage')
    }
  }
  
  render(){
    return <button onClick={this.props.requestToken}>Request Token</button>
  }
}

API

Workflow

Wrapper element containing a set of stages that make up the workflow. Only one stage will be rendered at a time. Transitions are handled by the stages themselves

  • name (Required): A unique identifier for this flow, to be used as a key in the Redux store. Must correspond to the name of a child Stage element.
  • initialStage (Required): The name of first stage to be rendered when this workflow initialises
  • reduxStoreKey *(Optional): Override the key that the workflow will use to store its state. Default: reduxWorkflow
  • onComplete *(Optional): A callback that will be invoked when the flow.complete() function is called from within any stage.

Stage

A child of the Workflow element that represents one step of the workflow.

Note that stages need not be specified in the order that they are visited and not all stages need to appear in the workflow (e.g. branching journeys)

  • name (Required): A unique identifier within this workflow for this stage, to be used as a key in the Redux store
  • component (Required): The component to be rendered when this stage is selected

Flow Accessor

An object provided to every rendered component (as the property: 'flow') specified by a Stage within a Workflow, which allows access to the workflow from within the Stage

  • name: The name of the Workflow
  • currentStage: String containing the name of the current rendered Stage
  • transitionTo(stage): Transitions the workflow to the specified Stage
    • stage (Required): The Stage name to transition to
  • saveStage(data, stage): Saves an arbitrary data object against the stage identifier in the Redux store
    • data (Required): Arbitrary object containing data to save (e.g.:{ FOO: 'BAR', BAZ: [ 'A', 'B'] })
    • stage (Optional): The Stage name to save this data against. If not specified, data will be saved against the currently active Stage. WARNING: Be careful using saveStage without a specific stage in the component lifecycle methods (e.g. componentWillUnmount) as the currentStage may have changed at this point, causing unexpected behaviour.
  • loadStage(stage): Loads data stored against the stage identifier in the Redux store. Returns undefined if there is no data saved against this Stage

    • stage (Optional): The Stage name to load this data against. If not specified, data will be saved against the currently active Stage
  • complete(): Completes the Workflow and triggers the provided onComplete callback

  • subscribe(actionType): Subscribe this workflow to observe actions of the given actionType

  • unsubscribe(actionType): Unsubscribe this workflow from being able to observe actions of the given actionType. Also clears any currently observed actions.
  • isActionObserved(actionType): Returns true if the action has occurred since the workflow subscribed to it

Example

A full demo application (with source) is included in the example directory at the project root.

To run the example:

cd example
yarn
yarn start

Then in your browser, navigate to http://localhost:3000

Contributing

TODO

License

MIT © pillie2005/redux-workflows (https://github.com/pillie2005/redux-workflows)

0.0.3

6 years ago

0.0.2

6 years ago

0.0.1

6 years ago