Quickstart

A quick dive into getting started with Lore

Step 3: Add Create Tweet Dialog

In this step we're going create a dialog that we can use to create tweets.

You can view the finished code for this step by checking out the dialogs.3 branch of the completed project.

Add Create Tweet Dialog

Run this command to generate the component we'll use for the dialog:

lore generate component CreateTweetDialog

Then open the file and update it to look like this:

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import _ from 'lodash';

export default createReactClass({
  displayName: 'CreateTweetDialog',

  propTypes: {
    title: PropTypes.node,
    description: PropTypes.node
  },

  getInitialState() {
    return {
      data: {
        text: ''
      }
    };
  },

  componentDidMount() {
    this.show();
  },

  show() {
    const modal = this.refs.modal;
    $(modal).modal('show');
  },

  dismiss() {
    const modal = this.refs.modal;
    $(modal).modal('hide');
  },

  request(data) {
    console.log(data);
  },

  onSubmit() {
    const { data } = this.state;
    this.request(data);
    this.dismiss();
  },

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  },

  render() {
    const { data } = this.state;

    return (
      <div ref="modal" className="modal fade">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" onClick={this.dismiss}>
                <span>&times;</span>
              </button>
              <h4 className="modal-title">
                Create Tweet
              </h4>
            </div>
            <div className="modal-body">
              <div className="row">
                <div className="col-md-12">
                  <div className="form-group">
                    <label>Message</label>
                    <textarea
                      className="form-control"
                      rows="3"
                      value={data.text}
                      placeholder="What's happening?"
                      onChange={(event) => {
                        this.onChange('text', event.target.value)
                      }}
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="modal-footer">
              <div className="row">
                <div className="col-md-12">
                  <button
                    type="button"
                    className="btn btn-default"
                    onClick={this.dismiss}
                  >
                    Cancel
                  </button>
                  <button
                    type="button"
                    className="btn btn-primary"
                    disabled={!data.text}
                    onClick={this.onSubmit}
                  >
                    Create
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

class CreateTweetDialog extends React.Component {

  constructor(props) {
    super(props);

    // set initial state
    this.state = {
      data: {
        text: ''
      }
    };

    // bind custom methods
    this.show = this.show.bind(this);
    this.dismiss = this.dismiss.bind(this);
    this.request = this.request.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  componentDidMount() {
    this.show();
  }

  show() {
    const modal = this.refs.modal;
    $(modal).modal('show');
  }

  dismiss() {
    const modal = this.refs.modal;
    $(modal).modal('hide');
  }

  request(data) {
    console.log(data);
  }

  onSubmit() {
    const { data } = this.state;
    this.request(data);
    this.dismiss();
  }

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  }

  render() {
    const { data } = this.state;

    return (
      <div ref="modal" className="modal fade">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" onClick={this.dismiss}>
                <span>&times;</span>
              </button>
              <h4 className="modal-title">
                Create Tweet
              </h4>
            </div>
            <div className="modal-body">
              <div className="row">
                <div className="col-md-12">
                  <div className="form-group">
                    <label>Message</label>
                    <textarea
                      className="form-control"
                      rows="3"
                      value={data.text}
                      placeholder="What's happening?"
                      onChange={(event) => {
                        this.onChange('text', event.target.value)
                      }}
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="modal-footer">
              <div className="row">
                <div className="col-md-12">
                  <button
                    type="button"
                    className="btn btn-default"
                    onClick={this.dismiss}
                  >
                    Cancel
                  </button>
                  <button
                    type="button"
                    className="btn btn-primary"
                    disabled={!data.text}
                    onClick={this.onSubmit}
                  >
                    Create
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

}

CreateTweetDialog.propTypes = {
  title: PropTypes.node,
  description: PropTypes.node
};

export default CreateTweetDialog;
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

class CreateTweetDialog extends React.Component {

  static propTypes = {
    title: PropTypes.node,
    description: PropTypes.node
  };

  constructor(props) {
    super(props);

    // set initial state
    this.state = {
      data: {
        text: ''
      }
    };

    // bind custom methods
    this.show = this.show.bind(this);
    this.dismiss = this.dismiss.bind(this);
    this.request = this.request.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  componentDidMount() {
    this.show();
  }

  show() {
    const modal = this.refs.modal;
    $(modal).modal('show');
  }

  dismiss() {
    const modal = this.refs.modal;
    $(modal).modal('hide');
  }

  request(data) {
    console.log(data);
  }

  onSubmit() {
    const { data } = this.state;
    this.request(data);
    this.dismiss();
  }

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  }

  render() {
    const { data } = this.state;

    return (
      <div ref="modal" className="modal fade">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" onClick={this.dismiss}>
                <span>&times;</span>
              </button>
              <h4 className="modal-title">
                Create Tweet
              </h4>
            </div>
            <div className="modal-body">
              <div className="row">
                <div className="col-md-12">
                  <div className="form-group">
                    <label>Message</label>
                    <textarea
                      className="form-control"
                      rows="3"
                      value={data.text}
                      placeholder="What's happening?"
                      onChange={(event) => {
                        this.onChange('text', event.target.value)
                      }}
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="modal-footer">
              <div className="row">
                <div className="col-md-12">
                  <button
                    type="button"
                    className="btn btn-default"
                    onClick={this.dismiss}
                  >
                    Cancel
                  </button>
                  <button
                    type="button"
                    className="btn btn-primary"
                    disabled={!data.text}
                    onClick={this.onSubmit}
                  >
                    Create
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

}

export default CreateTweetDialog;

This code will give us a Bootstrap dialog we can use to create tweets. When launched, it will fade into view, and allow us to enter the text for the tweet and submit it.

You can learn more about Bootstrap dialogs (called Modals) in the modal documentation on the Bootstrap website.

The important parts to call out here are the onSubmit() and request() methods shown below:

// src/components/CreateTweetDialog.js
request(data) {
  console.log(data);
},

onSubmit() {
  const { data } = this.state;
  this.request(data);
  this.dismiss();
},
// src/components/CreateTweetDialog.js
request(data) {
  console.log(data);
}

onSubmit() {
  const { data } = this.state;
  this.request(data);
  this.dismiss();
}
// src/components/CreateTweetDialog.js
request(data) {
  console.log(data);
}

onSubmit() {
  const { data } = this.state;
  this.request(data);
  this.dismiss();
}

Once the user submits the dialog, the request() method will be invoked and make the API request to send the data to the server. But for now, we're simply going to log the data to the console, and then dismiss the dialog.

Show the Dialog

Next, open the CreateButton component and import this dialog. Then update the onClick() method so that it shows the dialog instead of the text "Dialog Placeholder":

// src/components/CreateButton.js
...
import CreateTweetDialog from './CreateTweetDialog';
...
  onClick() {
    lore.dialog.show(function() {
      return (
        <CreateTweetDialog />
      );
    });
  }
...

Now when you click the button to create a dialog, a real dialog appears, and you can even fill it and submit the form, causing the data to be logged to the console.

The Create Action

Now we can launch a dialog and get the user input, but we aren't sending that input to the API yet. To do that we'll need to invoke the create action, which is done like this:

lore.actions.tweet.create({
  text: 'My tweet'
})

The argument you provide is an object with the attributes you want the tweet created with. In this example, we're creating a tweet and setting the text of the tweet to "My tweet".

Invoking this action will send a POST request to http://localhost:1337/tweets and include our attributes in the body of the request.

You can learn more about the create action here.

Save the Tweet

Let's finish the dialog by replacing the logging behavior with a real API call. Update the request() method of the CreateTweetDialog to look like this:

// src/components/CreateTweetDialog.js
request(data) {
  lore.actions.tweet.create(data);
},

Now when you submit a tweet, the action will send the data to the API. Try it out!

It's important to point out that the tweets you create will not appear on the page until you refresh the browser. We'll learn how to change that behavior later in the Quickstart, so that the new tweets appear in the Feed immediately.

Visual Check-in

If everything went well, your application should now look like this when you click the "create tweet" button:

Code Changes

Below is a list of files modified during this step.

src/components/CreateTweetDialog.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import _ from 'lodash';

export default createReactClass({
  displayName: 'CreateTweetDialog',

  propTypes: {
    title: PropTypes.node,
    description: PropTypes.node
  },

  getInitialState() {
    return {
      data: {
        text: ''
      }
    };
  },

  componentDidMount() {
    this.show();
  },

  show() {
    const modal = this.refs.modal;
    $(modal).modal('show');
  },

  dismiss() {
    const modal = this.refs.modal;
    $(modal).modal('hide');
  },

  request(data) {
    lore.actions.tweet.create(data);
  },

  onSubmit() {
    const { data } = this.state;
    this.request(data);
    this.dismiss();
  },

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  },

  render() {
    const { data } = this.state;

    return (
      <div ref="modal" className="modal fade">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" onClick={this.dismiss}>
                <span>&times;</span>
              </button>
              <h4 className="modal-title">
                Create Tweet
              </h4>
            </div>
            <div className="modal-body">
              <div className="row">
                <div className="col-md-12">
                  <div className="form-group">
                    <label>Message</label>
                    <textarea
                      className="form-control"
                      rows="3"
                      value={data.text}
                      placeholder="What's happening?"
                      onChange={(event) => {
                        this.onChange('text', event.target.value)
                      }}
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="modal-footer">
              <div className="row">
                <div className="col-md-12">
                  <button
                    type="button"
                    className="btn btn-default"
                    onClick={this.dismiss}
                  >
                    Cancel
                  </button>
                  <button
                    type="button"
                    className="btn btn-primary"
                    disabled={!data.text}
                    onClick={this.onSubmit}
                  >
                    Create
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

class CreateTweetDialog extends React.Component {

  constructor(props) {
    super(props);

    // set initial state
    this.state = {
      data: {
        text: ''
      }
    };

    // bind custom methods
    this.show = this.show.bind(this);
    this.dismiss = this.dismiss.bind(this);
    this.request = this.request.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  componentDidMount() {
    this.show();
  }

  show() {
    const modal = this.refs.modal;
    $(modal).modal('show');
  }

  dismiss() {
    const modal = this.refs.modal;
    $(modal).modal('hide');
  }

  request(data) {
    lore.actions.tweet.create(data);
  }

  onSubmit() {
    const { data } = this.state;
    this.request(data);
    this.dismiss();
  }

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  }

  render() {
    const { data } = this.state;

    return (
      <div ref="modal" className="modal fade">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" onClick={this.dismiss}>
                <span>&times;</span>
              </button>
              <h4 className="modal-title">
                Create Tweet
              </h4>
            </div>
            <div className="modal-body">
              <div className="row">
                <div className="col-md-12">
                  <div className="form-group">
                    <label>Message</label>
                    <textarea
                      className="form-control"
                      rows="3"
                      value={data.text}
                      placeholder="What's happening?"
                      onChange={(event) => {
                        this.onChange('text', event.target.value)
                      }}
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="modal-footer">
              <div className="row">
                <div className="col-md-12">
                  <button
                    type="button"
                    className="btn btn-default"
                    onClick={this.dismiss}
                  >
                    Cancel
                  </button>
                  <button
                    type="button"
                    className="btn btn-primary"
                    disabled={!data.text}
                    onClick={this.onSubmit}
                  >
                    Create
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

}

CreateTweetDialog.propTypes = {
  title: PropTypes.node,
  description: PropTypes.node
};

export default CreateTweetDialog;
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

class CreateTweetDialog extends React.Component {

  static propTypes = {
    title: PropTypes.node,
    description: PropTypes.node
  };

  constructor(props) {
    super(props);

    // set initial state
    this.state = {
      data: {
        text: ''
      }
    };

    // bind custom methods
    this.show = this.show.bind(this);
    this.dismiss = this.dismiss.bind(this);
    this.request = this.request.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  componentDidMount() {
    this.show();
  }

  show() {
    const modal = this.refs.modal;
    $(modal).modal('show');
  }

  dismiss() {
    const modal = this.refs.modal;
    $(modal).modal('hide');
  }

  request(data) {
    lore.actions.tweet.create(data);
  }

  onSubmit() {
    const { data } = this.state;
    this.request(data);
    this.dismiss();
  }

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  }

  render() {
    const { data } = this.state;

    return (
      <div ref="modal" className="modal fade">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" onClick={this.dismiss}>
                <span>&times;</span>
              </button>
              <h4 className="modal-title">
                Create Tweet
              </h4>
            </div>
            <div className="modal-body">
              <div className="row">
                <div className="col-md-12">
                  <div className="form-group">
                    <label>Message</label>
                    <textarea
                      className="form-control"
                      rows="3"
                      value={data.text}
                      placeholder="What's happening?"
                      onChange={(event) => {
                        this.onChange('text', event.target.value)
                      }}
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="modal-footer">
              <div className="row">
                <div className="col-md-12">
                  <button
                    type="button"
                    className="btn btn-default"
                    onClick={this.dismiss}
                  >
                    Cancel
                  </button>
                  <button
                    type="button"
                    className="btn btn-primary"
                    disabled={!data.text}
                    onClick={this.onSubmit}
                  >
                    Create
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

}

export default CreateTweetDialog;

src/components/CreateButton.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import CreateTweetDialog from './CreateTweetDialog';

export default createReactClass({
  displayName: 'CreateButton',

  onClick() {
    lore.dialog.show(function() {
      return (
        <CreateTweetDialog />
      );
    });
  },

  render() {
    return (
      <button
        type="button"
        className="btn btn-primary btn-lg create-button"
        onClick={this.onClick}>
        +
      </button>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import CreateTweetDialog from './CreateTweetDialog';

class CreateButton extends React.Component {

  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    lore.dialog.show(function() {
      return (
        <CreateTweetDialog />
      );
    });
  }

  render () {
    return (
      <button
        type="button"
        className="btn btn-primary btn-lg create-button"
        onClick={this.onClick}>
        +
      </button>
    );
  }

}

export default CreateButton;
import React from 'react';
import PropTypes from 'prop-types';
import CreateTweetDialog from './CreateTweetDialog';

class CreateButton extends React.Component {

  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    lore.dialog.show(function() {
      return (
        <CreateTweetDialog />
      );
    });
  }

  render () {
    return (
      <button
        type="button"
        className="btn btn-primary btn-lg create-button"
        onClick={this.onClick}>
        +
      </button>
    );
  }

}

export default CreateButton;

Next Steps

Next we're going finish adding the create dialog.