Thinkster
React

CRUD Examples with React & Redux

In this tutorial, we'll tie React & Redux up to a server for (C)reating, (R)eading, (U)pdating, and (D)estroying data

Valeri Karpov Up to date (Jan '17) React

This tutorial is a part of the "Build a Real World App with React & Redux" tutorial series.

CRUD is staple of modern web applications. In this tutorial, we'll show some real world use cases from the corresponding React+Redux RealWorld github repo. To start off, lets create a view where retrieved data can be displayed -- we'll do this specifically for the article view. The article view shows you an article and lets you comment on it. If you're the owner of the article, you can also edit it or delete it.

Article View

First off, we need to add the right HTTP calls to the agent.js file. For now, we'll only need 2: getting an article, and getting a list of the article's comments.

Update src/agent.js
// ...

const Articles = {
  all: page =>
    requests.get(`/articles?limit=10`),
  get: slug =>
    requests.get(`/articles/${slug}`)
};

// ...

const Comments = {
  forArticle: slug =>
    requests.get(`/articles/${slug}/comments`)
};

export default {
  Articles,
  Auth,
  Comments,
  setToken: _token => { token = _token; }
};

Next, lets write the top-level Article component. There's going to be a lot of sub-components for the Article view, so let's create a new directory.

Update src/Article/index.js
'use strict';

import ArticleMeta from './ArticleMeta';
import CommentContainer from './CommentContainer';
import { Link } from 'react-router';
import React from 'react';
import agent from '../../agent';
import { connect } from 'react-redux';
import marked from 'marked';

const mapStateToProps = state => ({
  ...state.article,
  currentUser: state.common.currentUser
});

const mapDispatchToProps = dispatch => ({
  onLoad: payload =>
    dispatch({ type: 'ARTICLE_PAGE_LOADED', payload }),
  onUnload: () =>
    dispatch({ type: 'ARTICLE_PAGE_UNLOADED' })
});

class Article extends React.Component {
  componentWillMount() {
    this.props.onLoad(Promise.all([
      agent.Articles.get(this.props.params.id),
      agent.Comments.forArticle(this.props.params.id)
    ]));
  }

  componentWillUnmount() {
    this.props.onUnload();
  }

  render() {
    if (!this.props.article) {
      return null;
    }

    const markup = { __html: marked(this.props.article.body) };
    const canModify = this.props.currentUser &&
      this.props.currentUser.username === this.props.article.author.username;
    return (
      <div className="article-page">

        <div className="banner">
          <div className="container">

            <h1>{this.props.article.title}</h1>
            <ArticleMeta
              article={this.props.article}
              canModify={canModify} />

          </div>
        </div>

        <div className="container page">

          <div className="row article-content">
            <div className="col-xs-12">

              <div dangerouslySetInnerHTML={markup}></div>

              <ul className="tag-list">
                {
                  this.props.article.tagList.map(tag => {
                    return (
                      <li
                        className="tag-default tag-pill tag-outline"
                        key={tag}>
                        {tag}
                      </li>
                    );
                  })
                }
              </ul>

            </div>
          </div>

          <hr />

          <div className="article-actions">
          </div>

          <div className="row">
            <CommentContainer
              comments={this.props.comments || []}
              errors={this.props.commentErrors}
              slug={this.props.params.id}
              currentUser={this.props.currentUser} />
          </div>
        </div>
      </div>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Article);

For this component, mapStateToProps will pull all the data from the article reducer that you'll write later, as well as the currently logged in user.

When loaded, this component will load the comments and article details.

This 'ArticleMeta' component will contain details about the article's author as well as any actions the user can take on the article, like editting or deleting.

marked is a library that compiles markdown into HTML - in order to get react to render raw HTML, we need to use this dangerouslySetInnerHTML property, because React sanitizes HTML by default.

Finally there's this CommentContainer component that's going to contain our comments.