react-at-rest

2.0.11 • Public • Published

React-at-Rest

A toolkit for building ridiculously fast web applications using React and RESTful APIs.

Codeship Badge

Documentation

Sample Projects

Clone the react-at-rest examples repo to get started! https://github.com/PayloadDev/react-at-rest-examples

The examples project contains sample code in both ES6 and CoffeeScript.

Main Concepts

  • React-at-Rest is composed of 3 main classes, working in concert: Store, DeliveryService, and RestForm.
    • Store: manages all AJAX requests and holds the data returned by the server.
    • DeliveryService: React Component that manages and simplifies communication with the Stores.
    • RestForm: React Component for building forms and managing RESTful data flow.
  • Uses Events for Store->Component communication.
  • Written in CoffeeScript, fully compatible with ES6.
  • Uses Bootstrap classes by default, but doesn't depend on Bootstrap.
  • Uses subclasses instead of mixins or composition.
  • Plays nicely with react-router

Requirements and Installation

React-at-Rest depends on react.

npm install --save react-at-rest react

Battle Tested and Pragmatic

React-at-Rest is a collection of powerful tools that make writing SPAs faster and simpler. You'll be amazed at what you can accomplish in a very short period of time.

React-at-Rest powers the Payload SPA at payload.net.

Getting Started

You can trivially consume a RESTful API using Store and DeliveryService

ES6

class BlogPosts extends DeliveryService {
 
  constructor(props) {
    super(props);
    // create a new Store which connected to an API at /posts
    this.postStore = new Store('posts');
  }
 
  // override bindResources to load all the resources needed for this component
  bindResources(props) {
    // retrieve all the posts from the Post Store
    this.retrieveAll(this.postStore);
  }
 
  render() {
    // show a loading message while loading data
    if !this.state.loaded
      return (<span>Loading...</span>)
 
    // iterate over all the blog posts loaded from our API
    let posts = this.state.posts.map((post) => {
      return (
        <div className="panel panel-default" key={post.id}>
          <div className="panel-heading">
            <h3 className="panel-title">{post.title}</h3>
          </div>
          <div className="panel-body">
            {post.body}
          </div>
        </div>
      )
 
    // render the posts
    return (
      <div>
        {posts}
      </div>
    )
  }   
}

CoffeeScript

class BlogPosts extends DeliveryService
 
  constructor: (props) ->
    super props
    # create a new Store which connected to an API at /posts 
    @postStore = new Store 'posts'
 
 
  # override bindResources to load all the resources needed for this component 
  bindResources: (props) ->
    # retrieve all the posts from the Post Store 
    @retrieveAll @postStore
 
 
  render: ->
    # show a loading message while loading data 
    return <span>Loading...</span> unless @state.loaded
 
    # iterate over all the blog posts loaded from our API 
    posts = for post in @state.posts
      <div className="panel panel-default" key={post.id}>
        <div className="panel-heading">
          <h3 className="panel-title">{post.title}</h3>
        </div>
        <div className="panel-body">
          {post.body}
        </div>
      </div>
 
    # render the posts 
    <div>
      {posts}
    </div>

Or to load a single resource:

ES6

class BlogPost extends DeliveryService {
 
  constructor(props) {
    super(props);
    // create a new Store which connected to an API at /posts
    this.postStore = new Store('posts');
  }
 
  // override bindResources to load all the resources needed for this component
  bindResources(props) {
    // retrieve the post from the Post Store by id
    this.retrieveResource({this.postStore, id: this.props.postId});
  }
 
  render() {
    // show a loading message while loading data
    if !this.state.loaded
      return (<span>Loading...</span> )
 
    // render the post
    return (
      <div>
        <div className="panel panel-default">
          <div className="panel-heading">
            <h3 className="panel-title">{this.state.post.title}</h3>
          </div>
          <div className="panel-body">
            {this.state.post.body}
          </div>
        </div>
      </div>
    )
  }
}

CoffeeScript

class BlogPost extends DeliveryService
 
  constructor: (props) ->
    super props
    # create a new Store which connected to an API at /posts 
    @postStore = new Store 'posts'
 
 
  # override bindResources to load all the resources needed for this component 
  bindResources: (props) ->
    # retrieve the post from the Post Store by id 
    @retrieveResource @postStoreid: @props.postId
 
 
  render: ->
    # show a loading message while loading data 
    return <span>Loading...</span> unless @state.loaded
 
    # render the post 
    <div>
      <div className="panel panel-default">
        <div className="panel-heading">
          <h3 className="panel-title">{@state.post.title}</h3>
        </div>
        <div className="panel-body">
          {@state.post.body}
        </div>
      </div>
    </div>

DeliveryService can load multiple resources in bindResources. Simply execute additional subscribeAll, subscribeResource, retrieveAll or retrieveResource methods.

Creating and updating resources

RestForm takes care of rendering create/edit forms and submitting to the API.

ES6

class BlogPostForm extends RestForm {
 
  //build your form using Reactified versions of regular HTML form elements
  render() {
    return (
      <form onSubmit={this.handleSubmit} className="form-horizontal">
        <Forms.TextAreaInput {...this.getFieldProps('body')}
          labelClassName='col-sm-4'
          inputWrapperClassName='col-sm-8'/>
        <Forms.TextInput {...this.getFieldProps('author')}
          labelClassName='col-sm-4'
          inputWrapperClassName='col-sm-8'/>
        <div className='text-right'>
          <button className='btn btn-primary'>Create Post</button>
        </div>
      </form>
    )
  }
}

CoffeeScript

class BlogPostForm extends RestForm
 
  # build your form using Reactified versions of regular HTML form elements 
  render: ->
    <form onSubmit={@handleSubmit} className="form-horizontal">
      <Forms.TextAreaInput {...@getFieldProps('body')}
        labelClassName='col-sm-4'
        inputWrapperClassName='col-sm-8'/>
      <Forms.TextInput {...@getFieldProps('author')}
        labelClassName='col-sm-4'
        inputWrapperClassName='col-sm-8'/>
      <div className='text-right'>
        <button className='btn btn-primary'>Create Post</button>
      </div>
    </form>

Then render your form component with either a blank model, or one retrieved from the API

ES6

// in <BlogPosts /> component
<BlogPostForm model={{}} store={this.postStore} />
 
// or to edit a blog posted loaded in a DeliveryService subclass
<BlogPostForm model={this.state.post} store={this.postStore} />

CoffeeScript

# in <BlogPosts /> component 
<BlogPostForm model={{}} store={@postStore} />
 
# or to edit a blog posted loaded in a DeliveryService subclass 
<BlogPostForm model={@state.post} store={@postStore} />

Credits

React-at-Rest was developed by the team at Payload and maintained by Ben Sargent.

Event code from the Backbone project.

Package Sidebar

Install

npm i react-at-rest

Weekly Downloads

45

Version

2.0.11

License

MIT

Last publish

Collaborators

  • ericchernuka
  • fortybillion