{"id":17772570,"url":"https://github.com/jamesplease/redux-simple-resource","last_synced_at":"2025-03-15T16:31:37.505Z","repository":{"id":57351584,"uuid":"76738395","full_name":"jamesplease/redux-simple-resource","owner":"jamesplease","description":"Project moved ---\u003e","archived":false,"fork":false,"pushed_at":"2017-06-23T02:41:17.000Z","size":201,"stargazers_count":11,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-07T01:47:33.963Z","etag":null,"topics":["api","crud","data","framework","http","redux","resource","rest","store"],"latest_commit_sha":null,"homepage":"https://github.com/jmeas/redux-resource","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamesplease.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-12-17T17:51:18.000Z","updated_at":"2021-05-07T15:43:51.000Z","dependencies_parsed_at":"2022-09-18T23:00:16.707Z","dependency_job_id":null,"html_url":"https://github.com/jamesplease/redux-simple-resource","commit_stats":null,"previous_names":["jmeas/redux-simple-resource"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Fredux-simple-resource","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Fredux-simple-resource/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Fredux-simple-resource/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Fredux-simple-resource/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamesplease","download_url":"https://codeload.github.com/jamesplease/redux-simple-resource/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221587194,"owners_count":16848054,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","crud","data","framework","http","redux","resource","rest","store"],"created_at":"2024-10-26T21:39:50.200Z","updated_at":"2024-10-26T21:39:50.961Z","avatar_url":"https://github.com/jamesplease.png","language":"JavaScript","readme":"# redux-simple-resource\n\nA Redux framework for interacting with remote resources.\n\n[![Travis build status](http://img.shields.io/travis/jmeas/redux-simple-resource.svg?style=flat)](https://travis-ci.org/jmeas/redux-simple-resource)\n[![Test Coverage](https://codeclimate.com/github/jmeas/redux-simple-resource/badges/coverage.svg)](https://codeclimate.com/github/jmeas/redux-simple-resource)\n\n### Table of Contents\n\n- [Motivation](#motivation)\n  - [Features](#features)\n  - [Why This Project?](#why-this-project)\n- [Getting Started](#getting-started)\n- [API](#api)\n  - [createResource()](#createresource-resourcename-options-)\n  - [xhrStatuses](#xhrstatuses)\n  - [updateResourceMeta()](#updateresourcemeta-resourcemeta-newmeta-id-replace-)\n  - [updateManyResourceMetas()](#updatemanyresourcemetas-resourcemeta-newmeta-ids-replace-)\n  - [upsertResource()](#upsertresource-resources-resource-id-idattribute-replace-)\n  - [upsertManyResources()](#upsertmanyresources-resources-newresources-idattribute-replace-)\n- [Guides](#guides)\n  - [Resource Names](#resource-names)\n  - [Resource Metadata](#resource-metadata)\n  - [XHR Statuses](#xhr-statuses)\n  - [Action Types](#action-types)\n  - [Structure of the Store](#structure-of-the-store)\n  - [Customizing the Default Reducers](#customizing-the-default-reducers)\n  - [Shallow Cloning](#shallow-cloning)\n  - [What is a \"simple\" resource?](#what-is-a-simple-resource)\n\n### Motivation\n\nUse this project to reduce Redux boilerplate when CRUD'ing remote resources.\n\n#### Features\n\n✓ Reducers, action types, and initial state created for you  \n✓ All features are opt-out: pick and choose what works for you  \n✓ Store [\"metadata\"](#resource-metadata) for resources and resource lists  \n✓ Works well with most APIs  \n✓ Unopinionated technology decisions  \n✓ Zero dependencies  \n\n#### Why this project?\n\nThere are numerous projects with the same goal as this library. The primary\ndifference between this library and other options is that this library\n[stores metadata about resources separately from the resource itself](#resource-metadata).\n\n### Getting started\n\nInstall this library through [npm ⇗](https://www.npmjs.com).\n\n`npm install redux-simple-resource`\n\nThen, import it and create a resource.\n\n```js\nimport {createStore, combineReducers, applyMiddleware} from 'redux';\nimport createResource from 'redux-simple-resource';\n\n// `options` is optional, and is documented in the \"API\" section of this README\nconst book = createResource('book', options);\n\n// Include its reducers in your store\nconst reducers = combineReducers({\n  book.reducers,\n  ...myOtherReducers\n});\n\nconst store = createStore(reducers);\n```\n\n## API\n\nThe default export of this library is `createResource`. There are several\nnamed exports, which are utilities that may help you when working with\nredux-simple-resource.\n\n### `createResource( resourceName, [options] )`\n\nThis is the default export of this library. Be sure to pass a singular version\nof your resource as `resourceName`. For instance, if this resource is for\n\"books\", then `resourceName` should be \"book\".\n\nFor multi-word resource names, use camel case. For instance, \"cat person\" should\nbe input as \"catPerson\".\n\nThe `resourceName` is used to generate action types. For more, read\n[the guide on Resource Names](#resource-names).\n\nThis method returns an object with the following properties:\n\n| Name | Description |\n|------|-------------|\n|reducer | A reducer that manages CRUD'ing the resource |\n|pluralForm | The pluralized name of `resourceName` ([Read more](#resource-names)) |\n|actionTypes | The CRUD action types to be used in user-defined action creators ([Read more](#action-types)) |\n|initialState | The initial state to be returned by reducer |\n\nThe second parameter, `options`, is an optional object that can be used to\ncustomize all of the default behavior. All options are optional. Read about the\ndifferent options below:\n\n##### `pluralForm`\n\nPass the pluralized version of your resource's name. By default, an \"s\" is\nappended to your resource's name. This name is used in action types for\nactions that affect more than one resource.\n\n```js\nconst person = createResource('person', {\n  pluralForm: 'people'\n});\n\nperson.pluralForm;\n// =\u003e `people`\n```\n\n```js\nconst cat = createResource('cat');\n\ncat.pluralForm;\n// =\u003e `cats`\n```\n\nThe plural form of a resource is used for action types that affect more than\none resource. For more, read [the guide on resource names](#resource-names).\n\n##### `supportedActions`\n\nAction types will be created for all CRUD actions, and the generated reducer\nsupports all of those actions. Sometimes, you won't need to support all CRUD\nactions on a resource. In those situations, you can use this to disable the\ncreation of particular action types. The five CRUD actions are:\n\n- `read`\n- `readMany`\n- `create`\n- `update`\n- `delete`\n\nPass `false` as the value for any of these to prevent those action types\nand reducers from being created.\n\nKeep in mind that you may not choose to use this option, even if your resource\nonly supports a subset of CRUD. You could simply choose to not use the other\ngenerated action types, and there would be no issues with you doing that. It's\nup to you.\n\n```js\n// Only \"READ_MANY\" action types and reducers will be created for this\n// \"cat\" resource.\nconst cat = createResource('cat', {\n  supportedActions: {\n    read: false,\n    readMany: true,\n    del: false,\n    update: false,\n    create: false\n  }\n});\n```\n\n##### `idAttribute`\n\nThe `id` property of a resource is special, as it is used to uniquely identify\nthat resource within your list of resources. This allows redux-simple-resource\nto keep your resources up-to-date as you make changes to them.\n\nBy default, redux-simple-resource looks for an attribute called \"id\". If your\nresource has an ID by some other value, you have two choices: you can either\nrename it to be ID, or use this option to update what redux-simple-resource\nwill look for.\n\n```js\nconst movie = createResource('movie', {\n  idAttribute: 'movieId'\n});\n```\n\n##### `initialState`\n\nAdditional initial state to add to the default initial state. The default\ninitial state is:\n\n```js\n{\n  // These are the actual resources that the server sends back.\n  resources: [],\n  // This is metadata about _specific_ resources. For instance, if a DELETE\n  // is in flight for a book with ID 24, then you could find that here.\n  // For more, see the Resource Meta guide in this README.\n  resourceMeta: {},\n  // This is metadata about the entire collection of resources. For instance,\n  // on page load, you might fetch all of the resources. The XHR status for\n  // that request would live here.\n  // For more, see the Resource Meta guide in this README.\n  resourceListMeta: {\n    readXhrStatus: xhrStatuses.NULL,\n    createXhrStatus: xhrStatuses.NULL\n  }\n}\n```\n\nExample usage is below:\n\n```js\nconst food = createResource('food', {\n  initialState: {\n    // Add this property to the default initial state. Custom action reducers\n    // could be used to modify this property. For more, see the `actionReducers`\n    // option.\n    peopleAreHungry: true\n  }\n});\n```\n\n##### `actionReducers`\n\nYou will likely need to handle more action types than the built-in CRUD action\ntypes. You can add in custom reducers to handle individual action types with\nthis option.\n\nYou may be used to `createReducers()` in Redux, which associates a reducer\nwith a particular \"slice\" of your store. This is similar, except each reducer\nis for a particular action type.\n\nPass in an Array of objects that define an `actionType` and `reducer`, where\nthe `actionType` is the type to associate with `reducer`.\n\nThe `reducer` is like any other reducer in Redux: it receives two arguments,\nand should return the new state.\n\n```\n(state, action) =\u003e newState\n```\n\nIf you use switch statements in your reducers, then this feature is just like\ndefining a new `case` block in your switch statement.\n\n```js\nconst pet = createResource('pet', {\n  actionReducers: [\n    {\n      actionType: 'SELECT_PET',\n      reducer(state, action) {\n        // Modify the state as necessary...\n        var newState = generateNewState(state);\n\n        // Then return it.\n        return newState;\n      }\n    }\n  ]\n})\n```\n\n### `xhrStatuses`\n\nThis is a named export from this library. It is an Object that represents\nthe different states that an XHR request can be in.\n\n```js\n// xhrStatuses\n{\n  PENDING: 'PENDING',\n  SUCCEEDED: 'SUCCEEDED',\n  FAILED: 'FAILED',\n  ABORTED: 'ABORTED',\n  NULL: 'NULL'\n}\n```\n\nFor an explanation of what each of these means, refer to\n[the XHR Statuses guide](#xhr-statuses).\n\nYou will usually use this object to determine the state of XHR requests for your\nresources. Let's see what this might look like:\n\n```js\nimport React, {Component} from 'react';\nimport {xhrStatuses} from 'redux-simple-resource';\n\n// Within a component's render, you may use it to conditionally render some JSX.\nclass MyComponent extends Component {\n  constructor(props) {\n    super(props)\n  }\n\n  render() {\n    // This is a value that is pulled from the store, and automatically kept\n    // up-to-date for you. For more, see the Guides section below.\n    const {resourceMeta} = this.props;\n\n    // Render loading text if a retrieve request is in flight\n    if (resourceMeta.readXhrStatus === xhrStatuses.PENDING) {\n      return (\u003cdiv\u003eLoading data\u003c/div\u003e);\n    } else {\n      return (\u003cdiv\u003eNot loading data\u003c/div\u003e);\n    }\n  }\n}\n```\n\n### `updateResourceMeta({ resourceMeta, newMeta, id, replace })`\n\nUse this method to update the metadata for a single resource. `resourceMeta`\nis **all** of the existing meta, `newMeta` is the new meta to assign to\nthe resource, and `id` is the ID of the resource that you are updating.\n\nThis does not directly modify the `resourceMeta` object; instead, it returns\na shallow clone.\n\n| Name | Required | Description |\n|------|-------------|----------|\n|resourceMeta | Yes | The current meta object for **all** resources |\n|newMeta | Yes | The new metadata |\n|id | Yes | The ID of the resource to update |\n|replace | No | Whether or not to replace any existing meta for this resource. Defaults to `false` |\n\n### `updateManyResourceMetas({ resourceMeta, newMeta, ids, replace })`\n\nSimilar to `updateResourceMeta`, but this enables you to update a list of `ids`\nwith the same `newMeta`. `resourceMeta` is **all** of the existing meta.\n\nIf `replace: true` is passed, then the existing meta is discarded, and what you\npass in will be all of the meta in the store.\n\nPass `replace: false` to keep all existing meta, and to merge in `newMeta` with\nany existing metadata for each resource. (default behavior)\n\nThis method does not enable you to update multiple IDs with different metadata.\n\n| Name | Required | Description |\n|------|-------------|----------|\n|resourceMeta | Yes | The current meta object for **all** resources. |\n|newMeta | Yes | The new metadata |\n|ids | Yes | An array of IDs to update |\n|replace | No | Whether or not to replace the current list, or to merge in the new data. Defaults to `false` |\n\n### `upsertResource({ resources, resource, id, idAttribute, replace })`\n\nInsert or update a resource to the list of resources.\n\n| Name | Required | Description |\n|------|-------------|----------|\n|resources | Yes | The current list of resources from the store |\n|resource | Yes | The new resource to add |\n|id | Yes | The id value of the new resource |\n|idAttribute | No | The id key of the new resource. Defaults to `\"id\"` |\n|replace | No | Whether or not to replace the resource (if it already exists). Defaults to `false` |\n\n### `upsertManyResources({ resources, newResources, idAttribute, replace })`\n\nInsert or update a list of resources to the list of resources.\n\n| Name | Required | Description |\n|------|-------------|----------|\n|resources | Yes | The current list of resources from the store |\n|newResources | Yes | The new resources to add |\n|idAttribute | No | The id key of the new resources. Defaults to `\"id\"` |\n|replace | No | Whether or not to replace the existing resource list, or to merge new with old. Defaults to `false` |\n\n## Guides\n\n### Resource Names\n\nThe first argument to `createResource` is `resourceName`, which is used to build\nthe CRUD action types for your resource. Under-the-hood, a plural version of the\nname is generated, which is returned to you as `pluralForm` on the resource\nobject (The default pluralized version is just ``${resourceName}s``, although\nyou can customize this with the [`pluralForm`](#pluralform) option).\n\nThe singular version of the resource name is used for action types that operate\non a single resource. The plural version is used for action types that operate\non multiple resources.\n\nFor instance, these are the action types that represent kicking off a request\nfor each of the built-in CRUD operations:\n\n```js\n// This operates on many resources\n`READ_MANY_BOOKS`\n\n// These operate on a single resource\n`READ_BOOK`\n`DELETE_BOOK`\n`UPDATE_BOOK`\n`CREATE_BOOK`\n```\n\nredux-simple-resource will snake case any camel case name that you pass in.\nFor instance, here's the read one action type for different resource names:\n\n| `resourceName` | read one action type |\n|----------------|----------------------|\n| cat            | READ_CAT             |\n| catperson      | READ_CATPERSON       |\n| catPerson      | READ_CAT_PERSON      |\n\nFor now, redux-simple-resource only supports reads of multiple resources,\nalthough you can add [`actionReducers`](#actionreducers) to manage multiple\nwrites. In the future, redux-simple-resource may come with built-in write-many\naction types and reducers (like `DELETE_BOOKS`).\n\n### Resource Metadata\n\nA resource typically has attributes associated with it. For instance, if your\napplication displays cats, then maybe each \"cat\" resource has a `name` and\n`age` associated with it. This is the data for that resource.\n\nIn client side applications, it is frequently the case that there is a lot of\n_other_ data associated with a particular resource, too. For instance, you may\nwant to track if the user has \"selected\" a particular cat in the UI, or if\nthey have made a request to delete a particular cat. This extra information is\n\"metadata.\"\n\nIn redux-simple-resource, metadata for each resource is stored in a separate\nlocation in the store. There is also metadata associated with the entire _list_\nof resources.\n\nredux-simple-resource comes with some built-in metadata for every resource out\nof the box. The metadata that it gives you are related to CRUD actions. If you\nfire off a request to delete a resource, for instance, then\nredux-simple-resource will take care of updating that resource's metadata with\n[the status of that XHR request](#xhr-statuses).\n\nYou can use this built-in metadata to easily show loading spinners, error\nmessages, success messages, and other UI features that conditionally appear\nbased on the status of XHR requests against a resource.\n\nAnd, of course, you can add in your own metadata, too.\n\n#### Individual Resource Metadata\n\nThe built-in metadata for individual resources is stored in each store slice\nas `resourceMeta`. This is an object, where each key is the ID of your object.\n\nThe built-in metadata for each resource is below:\n\n```js\n{\n  // The status of any existing request to update the resource\n  updateXhrStatus: xhrStatuses.NULL,\n  // The status of any existing request to fetch the resource\n  readXhrStatus: xhrStatuses.NULL,\n  // The status of an any existing request to delete the resource. Note that\n  // this will never be \"SUCCEEDED,\" as a successful delete removes the\n  // resource, and its metadata, from the store.\n  deleteXhrStatus: xhrStatuses.NULL\n}\n```\n\nFor instance, if we had two cats, and the first one had a request in flight to\ndelete it, then a piece of the store might look like:\n\n```js\n// store.cats\n{\n  resources: [\n    {id: 1, name: 'sarah'},\n    {id: 6, name: 'brian'},\n  ]\n  resourceMeta: {\n    1: {\n      updateXhrStatus: xhrStatuses.NULL,\n      // The request is in flight!\n      readXhrStatus: xhrStatuses.PENDING,\n      deleteXhrStatus: xhrStatuses.NULL,\n    },\n    6: {\n      updateXhrStatus: xhrStatuses.NULL,\n      readXhrStatus: xhrStatuses.NULL,\n      deleteXhrStatus: xhrStatuses.NULL,\n    }\n  },\n  ...otherThings\n}\n```\n\nYou are free to add custom metadata for every resource.\n\n\u003e Note: We do not recommend modifying the built-in meta values directly. Let\nredux-simple-resource manage those for you.\n\n#### Resource List Metadata\n\nSometimes, metadata needs to be associated with the entire list of resources.\nFor instance, in REST, to create a resource you must make a POST request to the\nlist of resources to. Following this pattern, the [XHR status](#xhr-statuses)\nof create requests is stored in the list metadata in redux-simple-resource.\n\nThe default list metadata is:\n\n```js\nresourceListMeta: {\n  readXhrStatus: xhrStatuses.NULL,\n  createXhrStatus: xhrStatuses.NULL\n}\n```\n\nYou can also store your own custom metadata on the list. For instance, if a\nuser can select a series of items in a list, you may choose to keep an array\nof selected IDs in the `resourceListMeta`. Or, you might instead add a\n`selected: true` boolean to each resource's individual meta. Both solutions\nwould work fine.\n\n### XHR Statuses\n\nHTTP requests go through a series of states, such as being in flight, or\nsucceeding, or being aborted. The metadata associated with each request reflects\neach of these statuses.\n\nThis library stores five possible states for each XHR request in Redux. These\nfive states are available as a [named export from the module](#xhrstatuses).\n\nThe states are:\n\n##### `PENDING`\n\nThe request has been sent, but a response has not yet been received. This state\nis colloquially referred to as being \"in flight.\"\n\n##### `SUCCEEDED`\n\nA success response was returned.\n\n##### `FAILED`\n\nAn error response was returned.\n\n##### `ABORTED`\n\nThe request was aborted by the client; a response will never be received.\n\n##### `NULL`\n\nThis status is applied to a request that has not begun. For instance, when a\nresource is first created, its read, update, and delete request statuses are\nall `NULL`, because none of those requests have been made.\n\nWhen requests have been made, there may also come a time when your application\nno longer cares about the result of a request. At that time, you have the option\nto set the XHR status to `NULL` by dispatching the `RESET` action type.\n\nNot all applications will need to `RESET` requests. Many (if not most) of the\ntime, you will not need to manually reset the status back to `NULL`. But the\noption is there if you need it.\n\n### Action Types\n\nredux-simple-resource creates CRUD-related action types for you.\n\nThe action types for read one, for instance, are:\n\n```js\n`READ_BOOK`\n`READ_BOOK_FAIL`\n`READ_BOOK_ABORT`\n`READ_BOOK_SUCCEED`\n`READ_BOOK_RESET`\n```\n\nThese five types reflect the five [XHR Statuses](#xhr-statuses), as each of\nthese actions will update the XHR Status in the resource's metadata.\n\nActions that operate on a single resource **must** include an \"id\" attribute to\nuniquely identify the resource being acted upon (although the name of the\nattribute can be configured using the [`idAttribute` option](#idattribute)).\n\nYou can attach as many properties as you want to your action types, but the\nfollowing properties have special meaning in redux-simple-resource:\n\n| Name | Used for | Description |\n|------|----------|-------------|\n| type | all      | The type of the action |\n| id   | read one, delete, update, create | Uniquely identifies the resource. For more, see [idAttribute](#idattribute) |\n| resource | read one, delete, update, create | The data for the resource |\n| resources | read many | An array of resources being affected by this action |\n| replace | read one, read many, update | Whether or not to replace existing data |\n\n#### The \"replace\" property\n\nAll `SUCCEED` action types support a `replace` property, which is whether or\nnot the updated data should replace existing data in the store. `replace` is\n`true` by default.\n\nFor single resources, passing `replace: false` will merge in the new data with\nthe existing data for that resource, if it exists. `replace: true` will\n\nFor multiple resources, `replace: false` will leave the existing list, but\nmerge in new resources with their existing versions, if they exist. New items\nwill be added at the end of the list. `replace: true` will completely remove the\nexisting list of resources, and their metadata, and replace it with the new\nlist.\n\n#### \"Start\" action type\n\nEach CRUD action has a start action type, which represents the start of a\nrequest. This will update the metadata for this particular action to be in\nan `xhrStatuses.PENDING` state.\n\nThese are the start action types:\n\n```js\n`READ_{RESOURCE}`\n`READ_MANY_{PLURAL_RESOURCE}`\n`CREATE_{RESOURCE}`\n`UPDATE_{RESOURCE}`\n`DELETE_{RESOURCE}`\n```\n\nAn example start action type is:\n\n```js\n{\n  type: READ_BOOK,\n  id: 5\n}\n```\n\n#### FAIL action type\n\nThis will update the metadata for this particular action to be in an\n`xhrStatuses.FAILED` state.\n\nThese are the five FAIL action types:\n\n```js\n`READ_{RESOURCE}_FAIL`\n`READ_MANY_{PLURAL_RESOURCE}_FAIL`\n`CREATE_{RESOURCE}_FAIL`\n`UPDATE_{RESOURCE}_FAIL`\n`DELETE_{RESOURCE}_FAIL`\n```\n\nAn example fail action type is:\n\n```js\n{\n  type: READ_BOOK_FAIL,\n  id: 5\n}\n```\n\n#### ABORT action type\n\nThis will update the metadata for this particular action to be in an\n`xhrStatuses.ABORTED` state.\n\n```js\n`READ_{RESOURCE}_ABORT`\n`READ_MANY_{PLURAL_RESOURCE}_ABORT`\n`CREATE_{RESOURCE}_ABORT`\n`UPDATE_{RESOURCE}_ABORT`\n`DELETE_{RESOURCE}_ABORT`\n```\n\nAn example fail abort action type is:\n\n```js\n{\n  type: READ_MANY_BOOKS_ABORT\n}\n```\n\n#### SUCCEED action type\n\nThis will update the metadata for this particular action to be in an\n`xhrStatuses.SUCCEED` state. It will also update the resources themselves in\nyour store.\n\nFor reads and updates, the data that you pass in will replace existing data in\nthe store unless you include `replace: false` in your action.\n\nExample success actions are:\n\n```js\n{\n  type: 'UPDATE_BOOK_SUCCESS',\n  id: 10,\n  resource: {\n    id: 10,\n    name: 'Twilight',\n    pages: 325\n  }\n}\n```\n\n```js\n{\n  type: 'READ_MANY_BOOKS_SUCCESS',\n  resources: [\n    {id: 2, name: 'Moby Dick'},\n    {id: 10, name: 'Twilight'},\n  ],\n  replace: false\n}\n```\n\n```js\n{\n  type: 'DELETE_BOOK_SUCCESS',\n  id: 10\n}\n```\n\n#### RESET action type\n\nThis action type is intended to be used to update a particular XHR status\nfrom a non-`NULL` value back to `NULL`. For instance, consider if a request\nfails, and you use the metadata in the store to conditionally render an alert\nto the user. If the user dismisses the alert, then you may wish to fire a\n`RESET` action type to reset the state back to `NULL`, which would cause the\nalert to disappear.\n\nAn example reset action is:\n\n```js\n{\n  type: 'DELETE_BOOK_RESET',\n  id: 10\n}\n```\n\n#### Custom Action Types\n\nCustom action types are also supported by this library. For an explanation,\nand an example, refer to\n[the API docs](https://github.com/jmeas/redux-simple-resource#actionreducers).\n\n#### Example Action Creator\n\nThis is an example action creator to update a cat resource. It is an [action\ncreator thunk](https://github.com/gaearon/redux-thunk).\n\nIt uses the [`xhr` module](https://github.com/naugtur/xhr) for making the\nrequest, although you can use any system that you want.\n\n```js\nimport xhr from 'xhr';\nimport cat from './cat-resource';\n\nfunction updateCat(id, data) {\n  return dispatch =\u003e {\n    dispatch({\n      type: cat.actionTypes.UPDATE_CAT,\n      id\n    });\n\n    const req = xhr.patch(\n      `/api/v1/cat/${id}`,\n      {json: data},\n      (err, res, body) =\u003e {\n        if (req.aborted) {\n          dispatch({\n            type: cat.actionTypes.UPDATE_CAT_ABORT,\n            id,\n          });\n        } else if (err || res.statusCode \u003e= 400) {\n          dispatch({\n            type: cat.actionTypes.UPDATE_CAT_FAIL,\n            id,\n          });\n        } else {\n          dispatch({\n            type: cat.actionTypes.UPDATE_CAT_SUCCEED,\n            resource: body,\n            id\n          });\n        }\n      }\n    );\n\n    return req;\n  };\n}\n```\n\nThis action type supports the full range of features in redux-simple-resource.\nBecause it returns the XHR, the user can abort the request. For instance, in\nyour component, you may have:\n\n```js\ncomponentWillMount() {\n  this.updateXhr = this.props.updateCat(2, {name: 'Felix'});\n}\n\ncomponentWillUnmount() {\n  if (this.updateXhr) {\n    this.updateXhr.abort();\n  }\n}\n```\n\n### Structure of the Store\n\nredux-simple-resource creates the overall structure of the store for resources.\nThe default initial state that is created for you represents this overall\nstructure:\n\n```js\n{\n  // These are the actual resources that the server sends back.\n  resources: [],\n  // This is metadata about _specific_ resources. For instance, if a DELETE\n  // is in flight for a book with ID 24, then you could find that here.\n  // For more, see the Resource Meta guide in this README.\n  resourceMeta: {},\n  // This is metadata about the entire collection of resources. For instance,\n  // on page load, you might fetch all of the resources. The XHR status for\n  // that request would live here.\n  // For more, see the Resource Meta guide in this README.\n  resourceListMeta: {\n    readXhrStatus: xhrStatuses.NULL,\n    createXhrStatus: xhrStatuses.NULL\n  }\n}\n```\n\nFor instance, if you're building a component that interacts with a particular\nbook resource, then you may use the following `mapStateToProps`:\n\n```js\nmapStateToProps(state, props) {\n  // Grab our book\n  const book = _.find(state.books.resources, {id: props.bookId});\n  // Grab its metadata\n  const bookMeta = state.resourceMeta[props.bookId];\n\n  // Pass that into the component\n  return {\n    book,\n    bookMeta\n  };\n}\n```\n\n### Customizing the Default Reducers\n\nSometimes, the default reducers may not do exactly what you want. Maybe you\nwant to handle a particular action in a different way. Or perhaps you want to\nadd more metadata with a different type.\n\nIf the way a particular action type is reduced is not what you want, then you\ndo not need to dispatch an action with that action type.\n\nIf you wish to add additional data to the store after a particular action, then\nwe recommend that you fire a separate, custom action immediately after the\ndefault one. For instance,\n\n```js\n// Dispatch the default action\ndispatch({type: 'READ_BOOK_SUCCESS', id: 5});\n// Dispatch your default action\ndispatch({type: 'READ_BOOK_SUCCESS_EXTRA_THINGS', id: 5});\n```\n\nIf you're worried about performance, give it a try. If you can demonstrate that\nthis pattern causes performance issues, then we will consider alternative\nsolutions.\n\ntl;dr: we do not recommend attempting to modify the built-in reducers. Either\ndon't dispatch those action types, or dispatch separate actions with custom\ntypes before or after the built-in types.\n\n### Shallow Cloning\n\nredux-simple-resource makes updates to the store via shallow cloning. This\nsystem works well if you make it a habit to never modify data from your store.\n\nThere are tools like Immutable to strongly enforce this, but you can also work\non teams on large products and not use Immutable.\n\nIf you absolutely need deep cloning, or Immutable support, rather than shallow\ncloning, then know that there are plans to add a hook to replace the shallow\ncloning with a custom cloning function. If this interests you, then follow along\non [this issue](https://github.com/jmeas/redux-simple-resource/issues/11).\n\n### What is a simple resource?\n\nThe notion of \"simple\" refers to APIs that do not have a specification for\ncompound documents. Compound documents are documents that contain primary\nresources, as well as resources that they have relationships with (one-to-one,\nmany-to-one, etc.).\n\nFor instance, consider fetching a \"cat\" resource. If you also wish to fetch\nthe cat's \"owner\" and its \"location\" in one request, what does the response look\nlike? Your API may not support this at all. Or it may merge all of the\nattributes from all of these objects together, requiring the client to pick them\napart. In both of those situations, the resource is what I am calling \"simple.\"\n\nOn the other hand, your API may return these other resources in a \"related\"\nkey, which has a list of other resources, as well as their type. This formal\nsupport for relationships enables you to build abstractions around them.\n\nIn summary, your API returns simple resources if:\n\n- it does _not_ support returning compound documents at all. Every request is\n  always against a single resource, and not the other resources it has\n  relationships with.\n- it _does_ support returning compound documents, but it is done in an ad hoc\n  way. In other words, there is no specification or format that applies to\n  every resource, and every relationship.\n\nBuilding APIs that support relationships in a consistent way is more difficult,\nand therefore, most APIs return simple resources.\n\nAn example of a system that _does_ provide a formal relationship definition is\n[JSON API]((http://jsonapi.org/)).\n\n#### What if my API supports more than \"simple\" resources?\n\nIf you're using a system like JSON API, where relationships are formally\ndefined by the API, then you still can use redux-simple-resource.\n\nJust be aware that redux-simple-resource will treat every response, even ones\ncontaining compound documents, as a single resource.\n\nYou have two options to deal with this:\n\n1. build an abstraction on top of redux-simple-resource to manage relationships\n2. use a different, JSON API-specific Redux framework\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesplease%2Fredux-simple-resource","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamesplease%2Fredux-simple-resource","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesplease%2Fredux-simple-resource/lists"}