Code Monkey home page Code Monkey logo

redux-saga-fetch's Introduction

redux-saga-fetch

Build Status Coverage Status

ory-redux-saga-fetch is a simple wrapper that reduces boilerplate code when using redux-saga in combination with async backend calls.

Installation

npm i --save ory-redux-saga-fetch

API

import { applyMiddleware, combineReducers, createStore } from 'redux'
import createSagaMiddleware from 'redux-saga'

import {
  createRequestAction,
  createSagaFetcher,
  isFetchFailure,
  isFetching,
  isFetchSuccess,
  selectPayload,
  selectErrorPayload
} from 'ory-redux-saga-fetch'

// Some exemplary functions that call a backend and return a promise.
// For more information on the fetch API, go here: https://github.com/bitinn/node-fetch
const getUsersFromAPI = () => fetch('http://myapi.com/users').then(res => res.JSON())
const getArticleFromAPI = (id) => fetch('http://myapi.com/articles/' + id).then(res => res.JSON())
const createArticleAtAPI = (article) => fetch('http://myapi.com/articles', { method: 'POST', body: JSON.stringify(article) }).then(res => res.JSON())

// Configuring our fetcher
const sagaFetcher = createSagaFetcher({
  users: {
    // Fetch is executed when the according action is triggered. Fetch expects a function that returns a Promise.
    fetcher: getUsersFromAPI
  },
  article: {
    // The action payload (see below) will be passed as the first argument.
    fetcher: (id) => getUsersFromAPI(id),
    // If any key of the group is fetching (in this case 'article' and 'createArticle') and
    // the other one is requested, the first one has to finish first before the second one gets fetched.
    group: 'article'
  },
  createArticle: {
    // This works with POST/PUT/DELETE/... methods as well
    fetcher: (payload) => createArticleAtAPI(payload),
    group: 'article'
  },
  contactEditor: {
    // supposed this fetcher needs to get the email address of the editor out of the store
    fetcher: (text, address) => writeEmail({ to: address, subject: 'send over redux-saga-fetch', text }),
    // selector is a regular redux selector taking the state and returning a partial state of choice
    selector: (state) => state.contacts.editor.emailAddress
})

// We need to wrap the root reducer in order to get sagaFetcher to work.
const rootReducer = combineReducers(
  sagaFetcher.wrapRootReducer({
    myOtherReducer1: (action, state) => ({ /*...*/ })
    myOtherReducer2: (action, state) => ({ /*...*/ })
  })
)

// This is regular redux stuff
const initialState = {}
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
  rootReducer,
  initialState,
  applyMiddleware(sagaMiddleware)
)

// We need to register the saga watchers of reduxSagaFetch
sagaMiddleware.run(sagaFetcher.createRootSaga())


// Now we're done, let's dispatch some actions!
createRequestAction('users')()
createRequestAction('article')(1234)
createRequestAction('createArticle')({ id: 12345, title: 'foo' })

// Now, the saga watchers will execute the API calls. In the meanwhile, you can check the status of each request using
isFetching('users')(store.getState()) // if true, the API call has not finished yet.
isFetchSuccess('users')(store.getState()) // if true, the API call resultet in Promise.resolve()
isFetchError('users')(store.getState()) // if true, the API call resultet in Promise.reject()

// Let's assume the API call has finished with an ok status code, and we want to see the result.
const users = selectPayload('users')(store.getState())

// Let's assume the API call has finished with an error (e.g. network or status code != 2xx), use this method to retrieve
// the error:
const error = selectErrorPayload('users')(store.getState())

Assuming you are using redux together with React, you could write your connector like this:

import React from 'react'
import { connect } from 'react-redux'
import {
  createRequestAction,
  createSagaFetcher,
  isFetchFailure,
  isFetching,
  isFetchSuccess,
  selectPayload
} from 'ory-redux-saga-fetch'

const Component = ({ getUsers, users, isFetchingUsers }) => (
  <div>
    <button onClick={getUsers()} />
    {isFetchingUsers ? 'still fetching...' : users}
  </div>
)


const mapStateToProps = (state) => ({
  isFetchingUsers: isFetching('users')(state)
  users: selectPayload('users')(state)
}

const mapDispatchToProps = (dispatch) => ({
  getUsers: createRequestAction('users')
})

export default connect(mapStateToProps, mapDispatchToProps)(Component)

redux-saga-fetch's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redux-saga-fetch's Issues

Add ability to chain/combine multiple fetchers into a group or dependency chain

Assuming

const sagaFetcher = createSagaFetcher({
  article: {
    fetcher: (id) => getUsersFromAPI(id)
  },
  createArticle: {
    fetcher: (payload) => createArticleAtAPI(payload)
  }
})

it would be very helpful if we could group those two fetchers into one, so we don't encounter race conditions on e.g. bad network connectivity.

Two ideas, the first one is grouping:

const sagaFetcher = createSagaFetcher({
  article: {
    fetcher: (id) => getUsersFromAPI(id),
    group: 'article'
  },
  createArticle: {
    fetcher: (payload) => createArticleAtAPI(payload),
    group: 'article'
  }
})

The second is dependency (gives us more accuracy over controls but is also more work to define):

const sagaFetcher = createSagaFetcher({
  article: {
    fetcher: (id) => getUsersFromAPI(id),
    dependsOn: ['createArticle']
  },
  createArticle: {
    fetcher: (payload) => createArticleAtAPI(payload)
  }
})

Invert state and key parameters in selectors

That way we can do this:

const mapStateToProps = createStructuredSelector({
  isFetching: isFetching(FETCH_DEPLOYMENTS),
  deployments: selectPayload(FETCH_DEPLOYMENTS)
})

instead of this:

const mapStateToProps = createStructuredSelector({
  isFetching: (state) => isFetching(state)(FETCH_DEPLOYMENTS),
  deployments: (state) => selectPayload(state)(FETCH_DEPLOYMENTS)
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.