GithubHelp home page GithubHelp logo

redux-api's Introduction

Redux-api

Flux REST API for redux infrastructure

Build Status NPM version Coverage Status

Introduction

redux-api solves the problem of writing clients to communicate with backends. It generates actions and reducers for making AJAX calls to API endpoints. You don't need to write a lot of boilerplate code if you use redux and want to exchange data with server.

Inspired by Redux-rest and is intended to be used with Redux.

Documentation

See DOCS.md for API documentation.

Use cases

Install

With npm:

npm install redux-api --save

With bower:

bower install redux-api --save

If you don't use tools like webpack, browserify, etc and you want to load redux-api manually, the best way to add redux-api to your project is:

<script src="(...)/redux-api.min.js"></script>
<script>
  window.ReduxApi = window["redux-api"];
  // or
  var ReduxApi = window["redux-api"];
  // initialization code
</script>

=======

Remote calls

redux-api doesn't bind you to a technology to make AJAX calls. It uses configurable adapters - a pretty simple function which receives 2 arguments: endpoint and options, and returns a Promise as result. The default adapter uses isomorphic-fetch, and has an implementation like this:

function adapterFetch(url, options) {
  return fetch(url, options);
}

However, you are not tied to using isomorphic-fetch. For instance, if you prefer to use jQuery, you can use the following adapter:

function adapterJquery(url, options) {
  return new Promise((success, error)=> {
    $.ajax({ ...options, url, success, error });
  });
}

This implementation allows you to make any request and process any response.

And of course you have to set up adapter to your redux-api instance before using.

  reduxApi(....).use("fetch", adapterFetch)

=======

Examples

examples/isomorphic - React + Redux + React-Router + Redux-api with webpack and express + github API

Example

rest.js

import "isomorphic-fetch";
import reduxApi, {transformers} from "redux-api";
import adapterFetch from "redux-api/lib/adapters/fetch";
export default reduxApi({
  // simple endpoint description
  entry: `/api/v1/entry/:id`,
  // complex endpoint description
  regions: {
    url: `/api/v1/regions`,
    // reimplement default `transformers.object`
    transformer: transformers.array,
    // base endpoint options `fetch(url, options)`
    options: {
      headers: {
        "Accept": "application/json"
      }
    }
  }
}).use("fetch", adapterFetch(fetch));

index.jsx

import React, {PropTypes} from "react";
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from "redux-thunk";
import { Provider, connect } from "react-redux";
import rest from "./rest"; //our redux-rest object

const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(rest.reducers);
const store = createStoreWithMiddleware(reducer);

function select(state) {
  return { entry: state.entry, regions: state.regions };
}

class Application {
  static propTypes = {
    entry: PropTypes.shape({
      loading: PropTypes.bool.isRequired,
      data: PropTypes.shape({
        text: PropTypes.string
      }).isRequired
    }).isRequired,
    regions: PropTypes.shape({
      loading: PropTypes.bool.isRequired,
      data: PropTypes.array.isRequired
    }).isRequired,
    dispatch: PropTypes.func.isRequired
  };
  componentDidMount() {
    const {dispatch} = this.props;
    // fetch `/api/v1/regions
    dispatch(rest.actions.regions.sync());
    //specify id for GET: /api/v1/entry/1
    dispatch(rest.actions.entry({id: 1}));
  }
  render() {
    const {entry, regions} = this.props;
    const Regions = regions.data.map((item)=> <p>{ item.name }</p>)
    return (
      <div>
        Loading regions: { regions.loading }
        <Regions/>
        Loading entry: {entry.loading}
        <div>{{ entry.data.text }}</div>
      </div>
    );
  }
}

const SmartComponent = connect(select)(Application);

React.render(
  <Provider store={store}>
    <SmartComponent />
  </Provider>,
  document.getElementById("content")
);

redux-api's People

Contributors

barrystaes avatar chee avatar dependabot[bot] avatar dkniffin avatar eduardoac avatar eps1lon avatar gvidon avatar hpaul avatar hsingh23 avatar jonny-novikov avatar kledal avatar lexich avatar loicmahieu avatar lvauvillier avatar marcgreenstock avatar means88 avatar moiseevigor avatar temnoregg avatar timbuckley avatar wvengen avatar xurei avatar ypcrumble avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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  avatar  avatar  avatar

redux-api's Issues

prevData been passed to transformer is always empty

For example, in a file api.js

export default reduxApi({
playback: {
    url: `${API_URL}/store/crums`,
    options: options,
    transformer: function(data, prevData, action){ 
      //prevData is retrieved from the stat as state.playback instead of state.api.playback
   }
  });

The state is stored as

{
 api:{
    playback: 
       data: [],
      ...
  }
}

Redux api assumes the state is the following according to this line in the code, https://github.com/lexich/redux-api/blob/master/src/actionFn.js#L101

{ 
    playback: 
       data: [],
      ... 
}

Also, wouldn't it be better to just allow the whole getState() like you do in the options method.

Redux API usage

Hi, I have one issue on passing headers to my request.

const headers = {
  'User-Agent': 'redux-api',
  'Accept': 'application/json',
  'Content-Type': 'application/json'
};

isLoggedIn: {
    reducerName: 'auth',
    url: `${URL}/api/isLoggedIn`,
    validation: function(data, callback) {
          // check data format
          let error;
          if (typeof data === 'object') {
            callback(null, data);
          } else {
            callback(new Error('Data isn\'t object'));
          }
    },
    options: {
      method: 'POST',
      headers: headers
    }
  }

I want to call my isLoggedIn api and get response.I am doing it in following way but not able to pass access_token as a header to my request,

export function checkAccessToken(token, storage) {
  let browserStorage = storage || localStorage;
  const accessToken = token || browserStorage.getItem(types.ACCESS_TOKEN);
  let headers = {
                  'Accept': 'application/json',
                  'Content-Type': 'application/json'
                };
  headers['access_token'] = accessToken;

  return (dispatch) => {
    if (token) {
      dispatch(loginUser());
      dispatch(auth.actions.isLoggedIn.sync({},{
         options: {
             headers: headers
        }
      },(res) => {
        dispatch(loginSuccess());
        dispatch(setAccessToken(res.accessToken, browserStorage));
        dispatch(userActions.setUsername(res.username));
        dispatch(userActions.setRole(res.role));
        dispatch(userActions.setResponsibilities(res.responsibilities));
      }));
      /*).catch((err) => {
        dispatch(loginFailure(err));
        dispatch(resetAccessToken(browserStorage));
      })*/;
    } else {
      dispatch(resetAccessToken(browserStorage));
    }
  };
}

Redux API usage

Hi, I am using redux-api to make requests in one of my projects.I've created one file which will have all the redux-api based api configurations.

const headers = {
    "User-Agent": "redux-api",
    'Accept': 'application/json',
    'Content-Type': 'application/x-www-form-urlencoded'
};

const URL =  // URL


export default reduxApi({
    login: {
        reducerName: 'auth',
        url: URL,
        validation: function (data, callback) {
            // check data format
            let error;
            if (typeof data == "object") {
                callback(data);
            }else{
                callback(error);
            }
        },
        transformer: function (data) {
            if (typeof data == "object") {
                return data;
            }
        },
        options: {
            method: "POST",
            headers: {
                "Accept": "application/json",
                "Content-Type": "application/x-www-form-urlencoded"
            }
        },
    }

Code from where I am calling this API is

function mockAuthentication(credentials,dispatch) {
  const reqParams = "username="+credentials.username+"&password="+credentials.password;
  const promise = new Promise((resolve, reject) => {
    dispatch(auth.actions.login.sync({},{ body: reqParams },(res,error)=>{
        if (res.type === 'ok') {
            resolve(res);
         } else {
           console.log("Into Login - reject"+error)
           reject(error);
         }
      }));
    });
  return promise;
}

If I do not use validation or transformer, I get null as a response in my callback. I've a few questions here -

  • Is the implementation correct?
  • Is there any way to call the API without using dispatch?
  • Does sync call return anything? Promise object as similar to what fetch does.

How to extend generated reducer with custom one?

Hi! This is more a question rather than issue.
My scenario as follows. I have a store element, say 'rules', connected to rest backend via your module.
Initialization looks as:

reduxApi = ReduxApi({
rules: {
crud: true,
url: "/api/rule",
transformer: data => _.indexBy(data, "id")
}
})

Later fetching is triggered as prescribed via

store.dispatch(reduxApi.actions.rules());

reducers extended with some custom ones via

_.extend(reduxApi.reducers, myReducers);
var store = Redux.createStore(Redux.combineReducers(reduxApi.reducers));

This approach works great unless I need to supply my custom reducers namely for store.rules object.
Say we need to delete some rule on server and upon receiving success response remove entry from local store.

In this case I have to dispatch two actions:

  • reduxApi.actions.rules.delete()
  • my custom one, lets call it deleteRuleFromLocalStoreAction()

So, I've stuck trying to understand what is the proper place for deleteRuleFromLocalStoreReducer reducer.

Source map includes reserved identifier

In the source map file, it's reading let values = [], which is throwing a syntax error on Firefox as let is a reserved identifier.

In the dist/redux-api.js file, we see the following example on line 1555.

var values = [];

However, the source map file dist/redux-api.js.map, shows the following (which is all on one line).

let values = [];\n\n

This is part of the qs library, so I think webpack is incorrectly creating an erroneous source map with this external library dependency.

If I get it to work, I'll send a PR. In the mean time, if anyone has any suggestion, or a fix, please pass it along.

how to abort already instantiated request?

Hi I am rquesting api using

api.actions.allUsers.request({},{
    headers: { 'access_token': Token, contains: 'a', skip: 0, limit: 20 }
  }); 

I want to abort this request when I unmount a component.How can I abort the request?

Handle standard CRUD functions

Hi,

First of all, great plugin, its working really well for us.
It would be nice if it could handle standard CRUD functions for a standard REST API that you could override as necessary. I currently have it set up like this:

const URL = 'https://example.com/api/v1'
function resourceOptions(resourceName) {
  return {
    url: `${URL}/${pluralize(resourceName)}/:id`,
    helpers: {
      update(id, resource) {
        const body = {};
        body[resourceName] = resource;
        const urlParams = { id };
        const params = {
          method: 'patch',
          body: JSON.stringify(body),
          headers: headers
        };
        return [urlParams, params];
      },

      create(resource) {
        const body = {};
        body[resourceName] = resource;
        const urlParams = {};
        const params = {
          method: 'post',
          body: JSON.stringify(body),
          headers: headers
        };
        return [urlParams, params];
      },

      delete(id) {
        const urlParams = { id };
        const params = {
          method: 'delete',
          headers: headers
        };
        return [urlParams, params];
      }
    }
  };
}

var options = {};
['test', 'post'].forEach(resource => {
  var plural = pluralize(resource);
  options[resource] = resourceOptions(resource);
  options[plural] = indexOptions(plural);
});

export default reduxApi(options)

This generates the rest API functions for tests and posts.
Adding a function like this into the plugin would mean even less boilerplate code ๐Ÿ˜„
All you would need then is a base URL, common headers, and an array of endpoint resource names.

Allow to set qs options

APIs have different ways to work with array parameters, and qs (which is used by redux-api) allows one to choose the array serialization method using the arrayFormat option.

Currently redux-api calls qs without any options, it would be useful to allow stringification options in urlTransform. See, for example, request's qsStringfyOptions. Perhaps as part of params?

How to get the pathvars used on a request?

Hi,

is there a way to get the pathvars used on a request?
For example, if I do api.actions.product({ 'sku': 123 }) I want to be able to know what sku was used when the reducer function is invoked (the actionFetch). Right now I only get { syncing: false }.

Or maybe I'm not using this right.

DELETE does not trigger postfetch

When a DELETE is done successfully, it does not trigger the postfetch. The response is:

Response: {
    ...
    ok:true
    status:204
    statusText:"No Content"
    ...
}

In #54 you mentioned that delete response should not be empty, but returning a 204 means not returning any response content, which is still valid.

HTTP response codes not respected

my api:

const api = reduxApi({
  auth: {
    url: '/auth/profile/',
  }})

my reducer:

export default function auth (state = initialState, action) {
  console.log('Reducer action', action)
  switch (action.type) {
    case api.events.auth.actionSuccess: // user has own information about access
        console.log('SUCCESS')
      return { ...state, profile: action.data };
    case api.events.auth.actionFail:
        console.log('FAIL')
      return { ...state, token: null }
    default:
      return state
  }
}

when server responds with 403, i get:

Reducer action { type: '@@redux-api@auth',
  syncing: true,
  request: { pathvars: undefined, params: {} } }
Reducer action { data: { detail: 'Invalid token.' },
  origData: { detail: 'Invalid token.' },
  type: '@@redux-api@auth_success',
  syncing: false,
  request: { pathvars: undefined, params: {} } }

Dispatch of helper doesn't return promise

dispatch(rest.actions.resource.get({id: 1})).then();
Gives us: Uncaught TypeError: Cannot read property 'then' of undefined
Dispatch of helper returns always undefined.

Again about custom reducers.

That's my redux-api config. It's some endpoint and CRUD operations - create and delete. To do "optimistic updates" (w/o syncing with API) i need to change the store, so i trying to call external reducers.

const rest = reduxApi({
  campaignList: {
    url: `clients/:clientId/campaigns`,
    options: getDefaultOptions('get', true)
  },
  createCampaign: {
    virtual: true,
    broadcast: [CREATE_CAMPAIGN_SUCCESS],
    url: `clients/:clientId/campaigns`,
    options: getDefaultOptions('post', true),
    postfetch: [
      function({ data, dispatch, getState }, cb) {
        const clientId = getState().auth.login.data.client_id;
        dispatch(common.actions.campaignList(
          { clientId: clientId }
        ), cb);
      }
    ]
  },
  deleteCampaign: {
    virtual: true,
    broadcast: [DELETE_CAMPAIGN_SUCCESS],
    url: `clients/:clientId/campaigns/:campaignId`,
    options: getDefaultOptions('delete', true),
  }
});

redux reducers

export const CREATE_CAMPAIGN_SUCCESS = 'campaign/CREATE_CAMPAIGN_SUCCESS';
export const DELETE_CAMPAIGN_SUCCESS = 'campaign/DELETE_CAMPAIGN_SUCCESS';

/*
 * Reducer
 */
function campaignReducer(state, action) {
  switch (action.type) {
  case CREATE_CAMPAIGN_SUCCESS:
    // here gonna mutate the state.
    console.log('campaignReducer state:', state);
    return state; // state.concat(action.data);
  case DELETE_CAMPAIGN_SUCCESS:
    // TODO: delete
    console.log('campaignReducer state:', state);
    return state;
  default:
    return state;
  }
}

export default campaignReducer;

And create:

const common = combineReducers(
  rest.reducers,
  campaignReducer);

const reducer = combineReducers({
  common,
  routing: routeReducer
});

export default reducer;

Is it possible to change the state of campaignList inside custom reducer to delete/update or create new element?

Can i call custom actions on fail?

broadcast: [CREATE_CAMPAIGN_SUCCESS, CREATE_CAMPAIGN_FAIL] ?

async helper does not pass data.

First of all thanks a lot for this wonderful library
I am trying out following snippets

    async() {
        const {dispatch} = this;
        return (cb)=> {
          dispatch(rest.actions.logger((err)=> {
            const args = [{id: 1, name: "admin"}];
            cb(err, args);
          }));
        };
      }

and

async(dispatch, 
  (cb)=> rest.actions.test(cb),
  rest.actions.test2
).then((data)=> async(rest.actions.test3));

in both the cases I am not able to get data passed to next then block.

data is always undefined, I presume it should be fetched data.
In my case my one entry depends on few other end points so I need to ensure values are there.

Handling errors like 'unauthorized' globally

Hi, thank you for a great plugin. I was wondering if there is a nice way how to handle errors globally. For example if the cookie expiry I would love to see some global handling of unauthorised request in one spot. Event 'actionFail' seems too manual to put everywhere.

is there a way to modify the url in a helper?

consider an api like:

GET /users
GET /users/:id
POST /users/:id/invalidate
POST /users/:id/forgot_password

i've managed to emulate this behavior like so:

user: {
  url: `/users/:id/(:method)`,
  virtual: true,
  helpers: {
    invalidate({id}) {
     return [{id: id, method: 'invalidate'}, { method: "post"} ];
    },
    forgot_password({id}) {
     return [{id: id, method: 'forgot_password'}, { method: "post"} ];
    }
  }
}

but this doesn't work if i need to also access the resource (ie, without virtual=true). is there a better way to do this (or would you be open to a pr to add this functionality?)

Recommended method for handling paging?

Thanks for the great work on this library!

I'm attempting to use it with an API supporting paging but I don't see any information about how to extend the data in the store.

For example, fetching page 1, and then page 2 would replace page 1 with page 2...

Is there an obvious way around this that I'm missing?

Thanks

Asynchnous transformer

Hello, trying to work with redux-api using whatwg fetch polyfill as adapter.
The issue is that build-in transformer functionality provides synchronous interface.
When we deal with whatwg fetch, response body (json in my case) is being obtained via promise, so there is no elegant way to obtain it within transformer, modify and return in a transformed form.

transformer: data => {
  data.json().then(json=>{
     // here i need to make any transformations on json
     // ready im ready to provide results
 })  
}

CORS is broken

When using 0.9.0's new crud option with rack-cors on the server, I found it to be broken. Solution: use upper-case http methods (POST instead of post). It looks like case matters.

Change redux state without actual API call.

Hello, first of all, this lib is really great time saver, hope you'll continue development.

Problem:

I store session data in cookes so if i reload the page state could be restored. It's kinda trivial if i use pure Redux, but i can not figure out if it's possible via redux-api.

    const authData = Cookies.getJSON(AUTH_COOKIE_PATH);
    if (authData !== undefined) {
      this.props.dispatch(...? how dispatch success action to set store with data 'authData' ?...);
    }

shared action between reducer mutates data in transformer?

thanks for the great library, i am experiencing a bug that while entirely could be my own fault, seems to happen in the transformer, i'll try to explain without detailed code below,

reducer 1 listens for api.events.categories.actionSuccess and puts that data into an object
reducer 2 listens for api.events.categories.actionSuccess and puts that data into an object
reducer 2 also changes the data to toggle on certain categories that the user has chosen (comes from different api call).

the issue is that in the transformer i wrote i can see the data from reducer 2 mutating what the transformer is returning, which seems strange. so reducer 1 gets the mutated data from reducer two via the action.data in the api.events.categories.

i'm curious if anyone has experienced this or something like it

Many actions for one reducer?

One issue I still have with this project is that in many cases I have many actions for data set (add user, edit user, remove user for example) how does one get all that into a single reducer?

Handling errors with helpers

I have next api:

export default reduxApi({
  project: {
    url: `${URL}/projects/(:id)`,
    options: options,
    helpers: {
      create(data, callback) {
        return [{}, {method: 'POST', ...data}, callback]
      }
    }
  }
}).use("fetch", adapterFetch(fetch));

And call it next way:

dispatch(forgeApi.actions.project.create({
      project: {
        name:        values.name,
        description: values.description,
        startDate:   values.startDate,
        endDate:     values.endDate,
      }
    }, (error, data) => {
      console.log("error: ", error)
      console.log("data: ", data)
      return Promise.resolve(data)
    }))

I have response 422 from server (as it should be with test data) but in callback I have null error and data with parsed json from server. It's ok to have parsed json but I wonder why error is empty. In this case I don't now if request was success or not and how I should interpret data from response as created object or as error description.

Url params not included in final url

If you set rootUrl when init reduxApi the params send to action are no longer attached to action url.

client: {
    url: `/profiles/clients/(:id)`,
}
.....
.init(fetch, false, 'some-ip');

Then dispatch action

dispatch( rest.actions.client({id: 2) );

It end up with this url some-ip/profiles/clients/(:id) not this some-ip/profiles/clients/2.

The solution is on https://github.com/lexich/redux-api/blob/master/src/actionFn.js#L54

const urlObject = libUrl.parse(url); // url should be `urlT`

How to use the crud helper functions

Redux-api really makes using REST APIs easy.
I read the documentation for the helper functions. So far I got them working only via dispatch like:

 dispatch(rest.actions.group({}, {
      body: JSON.stringify({ name: this.state.name})
    }));

The following does not issue a REST request:
rest.actions.groups.get({ id: this.props.params.groupId})
How are the helper functions supposed to be used.

Thanks,
Andreas

restApi.use('fetch', adapterFetch(fetch)) is required

Hi,
The documentation could be more explicit about the fact that the application MUST call restApi.use('fetch', adapterFetch(fetch)) - if you don't you get an error like can't call apply on undefined.

Your root index.js should also reexport the adapterFetch function otherwise the only way to get to it is to put the entire path which is somewhat weird: import adapterFetch from 'redux-api/lib/adapters/fetch';

PS: so far, I really like the philosophy behind your API and I like the fact that you build the reducers automatically.

How to implement create and update?

For instance i have a code like this:

  // domains
  domainListDetails: {
    url: `clients/:clientId/campaigns/:campaignId/domainlists/4/domains`,
    options: getDefaultOptions('get'),
  },  // store array or results
  addDomainToDomainList: {
    reducerName: 'domainListDetails',
    url: `clients/:clientId/campaigns/:campaignId/domainlists/4/domains`,
    options: getDefaultOptions('post'),
  }, // this will overwrite array in store by one updated result

but when i dispatch addDomainToDomainList the API returned one updated result which passed to store and just overwrite list already stored inside.

How can i implement update one element in the list?

Dispatching an action on error (error handling)

I'm trying to come up with a clean way to handle errors returned by the API. When a request fails, I'd like to show a notification that removes after a timeout. For my purpose, that means dispatching an action (which is thunk-based to be able to submit the hide-notification action after a timeout, e.g. re-notif).

My current solution is to modify all rest-api actions and add an error-handling callback, making sure to call the original callback if it was supplied. It's ugly (I guess it could be improved a bit, but main points are the need to change all existing actions, and the way in which the callback is hooked).

const rest = reduxApi({ /* ... */ });

function withErrorHandler(actionCreator) {
  return function() {
    const callback = typeof(arguments[arguments.length-1]) === 'function' ? arguments[arguments.length-1] : undefined;
    const creatorArgs = callback ? [...arguments].slice(0, -1) : arguments;
    return (dispatch) => {
      dispatch(actionCreator(...creatorArgs, (error, data) => {
        error && dispatch(notifs.notifSend({message: error.message, kind: 'danger', dismissAfter: 3000}));
        callback && callback(error, data);
      }));
    };
  };
}

export default {...rest, actions: Object.keys(rest.actions).reduce(
  (memo1, key1) => { return {...memo1, [key1]: Object.keys(rest.actions[key1]).reduce(
    (memo2, key2) => { return {...memo2, [key2]: withErrorHandler(rest.actions[key1][key2]) }; }, {}
  )}}, {}
)};

I've tried others things that didn't work out:

  • Wrapping my final reducer in a function, that whenever a redux-api failure action is dispatched, the state is modified directly to show the action. Doesn't work because I need thunk-based actions, which make the reducer impure.
  • Subscribing to the store, doesn't work because there are no actions to watch, just state changes (and not all actions may trigger the subscriber means possibly losing errors).

Any suggestions to improve this?

Sync with different query parameters

I'm accessing an index endpoint with search parameters, and trigger a sync() whenever a different filter is selected on the client-side. It appears that redux-api's sync() never calls out to the server when it's already been loaded, even when the query parameters are different.

const rest = reduxApi({
  articles: {
    url: '/api/v1/articles',
    transformer: transformers.array,
  }
});

class Foo extends React.Component {  

  componentDidMount() {
    this.props.dispatch(rest.actions.articles.sync({category_id: 1}));
  }

  // ...

  _onClick(category_id) {
    this.props.dispatch(rest.actions.articles.sync({category_id: category_id}));
  }

}

// connect ...

This always keeps the items in the store with category_id=1, even if a sync({category_id: 2}) is called later.

Setting the article's sync key makes it work though:

  _onClick(category_id) {
    this.props.articles.sync = false; // hack
    this.props.dispatch(rest.actions.articles.sync({category_id: category_id}));
  }

I wonder, wouldn't it be better if sync() returned the data directly (without another http roundtrip) only if the parameters are equal?

Chained requests

I'm trying to get a chain of requests going, each of which has to complete before the next one fires.
Looking at the docs here: https://github.com/lexich/redux-api/blob/master/DOCS.md, it looks like the intended use is as follows:

async(
  dispatch, () => rest.actions.car.delete(carId)
).then((data)=> {
  debugger
  async(rest.actions.invoice({ id: this.props.invoice.data.id }))
});

Unfortunately that debugger is never hit, the then function is never called.
Any idea what's going on here?

Caching?

Hi,

Trying it out for a while now and it works great!
The only issue I got with it is that there's no caching functionality?
Is it possible to add this functionality in the near future?
Thanks again!

RFC

Typical cases.

  • authorization chain
  • integration with redux-persist
  • websockets
  • Internal api in helpers
  • integration with 3d party serialization

Feature requests.

  • #166 Retry request option
  • #120 Make using async tools deprecated
  • #90 Fix documentation about transformers

Does not handle simultaneous requests

For example, you cannot make the following sequence of gets
GET /item/:id
GET /item/:id
where :id is in [1,2]

This is because, the first request may still be loading when the second request is made. Redux api also does not retry failed requests or provide an error message for a request that could not be made.

I have an example of a reducer i use as a substitute.

export default function trips(state={}, action) {
switch(action.type) { 
  case api.events.trip.actionFetch:
    return Object.assign({}, state, {
      [action.request.pathvars.id]:{
        loading: true,
        ['data']: {...state[action.request.pathvars.id]['data'], ...action.data}
      }
    }) 
  case api.events.trip.actionSuccess:
  return Object.assign({}, state, {
      [action.request.pathvars.id]:{
        loading: false,
        ['data']: {...state[action.request.pathvars.id]['data'], ...action.data}
      }
    }) 
  case api.events.trip.actionFail:
    return Object.assign({}, state, {
      [action.request.pathvars.id]:{
        loading: false,
        error: action.error,
        ['data']: {...state[action.request.pathvars.id]['data'], ...action.data}
      }
    }) 
  default:
     return state;
    }
}

No Introduction in README.md

The readme has detailed API docs to tell me how to use redux-api. But it does not introduce it.

  • What is redux-api for,
  • what problem does it solve, and
  • why i should want to use redux-api to solve that?

I just found it due to its name (API and Redux) but i'm still not sure what it does exactly.. whether it is what i was looking for.

delete action fails

while using the following code, i can successfully delete something from my api, but redux-api gives me a projectsDelete_fail action. apologies but i don't see anything in the docs about deletes being different.

projectsDelete: { helpers: { byId(id) { return [{id: id}]; } }, options: { method: 'delete' }, postfetch: [ function({ dispatch, actions }) { dispatch(actions.projectsGet()); } ], url: 'projects/:id', }

Merging state with ImmutableJS

Hi!

When returning the data I would like the data to be merged into the immutable state.
I'd like to do something like this: state.getIn(['api', 'settings', 'data'])

Instead the reducer is initialized with a normal js object and the immutable data is passed to that object. This breaks my immutable chain.

Api setup
screen shot 2016-02-15 at 15 05 42

Redux devtools:
screen shot 2016-02-15 at 15 04 28

Or is there some other way that I missed to achieve this?

Response headers not passed

I face a problem for two days and realized that redux-api doesn't pass headers even when request was successfully or failed.

So when I dispatch this
screen shot 2015-10-21 at 19 42 46
Error it's just Unexpected end of input.

But I need to see response status code which is 401 Unauthorized so I could dispatch an action to show a Login Modal
screen shot 2015-10-21 at 19 47 38

That's how getDetails is configured.

  getDetails: {
    url: `${apiEndpoint}/user`,
    reducerName: 'account',

    options: {
      headers: headers,
    },

    prefetch: [
      attachAuthorization,
    ],

    validation: (data, cb) => {
      if (data.error) {
        cb(data);
      }

      cb();
    },
  },

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.