Skip to content
This repository has been archived by the owner on Aug 2, 2024. It is now read-only.
/ react-at-rest Public archive

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

License

Notifications You must be signed in to change notification settings

PayloadDev/react-at-rest

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

React-at-Rest

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

Codeship Badge

WARNING: react-at-rest is deprecated

This package is deprecated and is no longer being maintained.

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 @postStore, id: @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.