Skip to content

Redux Agent  declarative, middleware-free effect model

In UI development, React lets you describe the interface and have the machine worry about drawing it. A simple and powerful model which unfortunately stops at visual I/O. Non-visual I/O such as network requests is usually done in thunks/sagas/epics (scattering logic across middlewares and reducers) or in UI components (coupling them to remote APIs).

Redux Agent extends React’s model to non-visual I/O: describe a network request, a storage operation, a websocket message, … and let the machine worry about performing it. Logic stays in the reducer, components stay lightweight, and it’s easy to see what state triggers which effect.

Redux Agent clocks at just 3kB. It doesn’t introduce middlewares and doesn’t involve exotic concepts; it has only one basic abstraction (the “task”) and works wherever Redux does, regardless of UI framework. Typical usage is as simple as:

import { addTask } from 'redux-agent'

const getTodo = (id) => ({ 
  type: 'http', 
  method: 'get', 
  url: `https://jsonplaceholder.typicode.com/todos/${id}`,
  actions: { 
    success: 'FETCH_TODO_SUCCESS',
    failure: 'FETCH_TODO_FAILURE'
  }
})

const reducer = (state, action) => {
  switch(action.type) {

    case 'FETCH_TODO':
      return addTask(state, getTodo(1))

    case 'FETCH_TODO_SUCCESS':
      return {
        ...state,
        items: [ ...state.items, action.payload ]
      }

Try it

See Redux Agent in action in one of these interactive examples:

Q&A

“I thought that the reducer should stay free from side effects?”

Yes. In fact, addTask in the example above doesn’t perform any effect, it only stores a task description in the state which is later used by the runtime to perform the effect, just like you normally store data in the state which is later used to render the UI.

“What if I want to not just add a task but also modify the state in other ways?”

addTask simply returns a new state that includes the desired task. You can derive further states as usual:

    case 'FETCH_TODO':
      const s1 = addTask(state, getTodo(1))
      return { ...s1, isLoading: true }

Or make it even easier on the eyes with Redux Agent’s built-in support for Immer.

“How well does it scale to complex flows?”

Redux Agent provides building blocks and is not prescriptive about how you arrange them. That said, one style it turned out to be a great fit for is state charts, see (draft) Writing a robust, reusable login flow with redux‑agent and state charts for an example.

“Is this similar to Elm?”

Quite. But whereas Elm’s reducer returns the new state plus commands, Redux Agent considers active tasks an integral part of the application state and therefore keeps them in the state tree.

“Is this similar to redux-loop?”

Yes and no. Yes, since both make the reducer the single source of logic. No, since redux-loop changes the reducer’s API so it can return state plus “commands” (like Elm), whereas Redux Agent uses vanilla Redux APIs.

Documentation

License

MIT

Credits

reduceReducers by Tim Cheung. Icon by Setyo Ari Wibowo.