ReactJS: How to best handle JSON response of newly created object

By : Joseph
Source: Stackoverflow.com
Question!

I have three React components: the first is a container (NoteContainer component) which is responsible for rendering my objects (Note component) in the UI. The data is obtained as JSON via AJAX GET. The last component is a form (NoteForm) which creates new objects (via AJAX POST).

The response from the POST is only the JSON representation of the newly created object, not the JSON for all of the objects.

Should the NoteForm send the JSON response from creating a new object to the NoteContainer which would append it to its state.data and re-render , or should the NoteContainer request the full list of objects and update its state date entirely?

I would presume the first way is better since it does not require requesting data which is already present in the state of NoteContainer. However, I'm still not sure of the "best" way to handle this. Should I give NoteContainer another function, something like addNewNote, which would take the JSON data from the NoteForm and append it to state.data?

I'm new to React so I apologize if this is not a clear question. Here are my components:

var NoteContainer = React.createClass({
  getInitialState: function(){
    return {data: []};
  },
  componentDidMount: function() {
    $.ajax({
      url: this.props.url, 
      dataType: 'json',
      cache: false,
      success: function(data){
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err){
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  render: function(){
    var noteNodes = this.state.data.map(function(note){
      return (
        <Note title={note.title} body={note.body} />
      );
    });
    return (<div className='noteContainer'>{noteNodes}</div>);
  }
});

var Note = React.createClass({
  render: function(){
    return (
      <div className="note" >
        <h1>{this.props.title}</h1>
        <p>{this.props.body}</p>
      </div>
    );
  }
});


var NoteForm = React.createClass({
  getInitialState: function(){
    return {'title': '', 'body': ''}
  },
  handleTitleChange: function(e){
    this.setState({title: e.target.value});
  },
  handleBodyChange: function(e){
    this.setState({body: e.target.value});
  },

  handleSubmit: function(e){
    e.preventDefault();
    var note = {
      title: this.state.title,
      body: this.state.body};
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: note,
      success: function(data){
        // Send data to NoteContainer? 
      }.bind(this),
      error: function(xhr, status, err){
        console.error(this.props.url, status, err.toString());
      }.bind(this)
  });
},

  render: function(){
    return (
      <form> 
        <input
          type='text'
          placeholder='Title'
          value={this.state.title}
          onChange={this.handleTitleChange} />
        <textarea onChange={this.handleBodyChange}>
          {this.state.body}
        </textarea>

      </form>
    );
  }
});
By : Joseph


Answers
What @xCrZx is suggesting is that you pull the state outside of the individual components and have a one or more top-level stores to maintain state. The simplest (i.e. "vanilla") example of this would be if your NoteContainer was a parent of NoteForm. Then you could simply pass a callback from NoteContainer to NoteForm:

var NoteContainer = React.createClass({
  createNote: function() {
    ...
  },
  render: function() {
    return (
      ...
      <NoteForm createNote={this.createNote}>
      ...
    );
  }
});

var NoteForm = React.createClass({
  props: {
    createNote: React.PropTypes.func.isRequired
  },
  render: function() {
    return (
      ...
        onClick={this.props.createNote}
      ...
    );
  }
});

However, that of course only works if the relationship actually exists. Now let's take a look at Reflux, where you create central stores (and actions to go with them) to keep data, and components "listen" to the stores.

var NoteActions = Reflux.createActins([
  'createNote',
  'getNotes'
]);

var NoteStore = Reflux.createStore({
  listenables: [NoteActions],
  init: {
    // Notes is an empty array by default
    this.notes = [];
  },
  getInitialState: function() {
    return {
      notes: this.notes
    };
  },
  onCreateNote: function(noteFormData) { ...POST here, save note JSON to this.notes on success... },
  onGetNotes: function() { ..GET here for the initial load...}
});

var NoteForm = React.createClass({
  render: function() {
    ...
      onClick={NoteActions.createNote(noteFormData)}
    ...
  }
});

var NoteContainer = React.createClass({
  mixins: [Reflux.connect(NoteStore)],
  componentDidMount: function() {
    NoteActions.getNotes();
  },
  render: function() {
    return: function() {
      .. same that you have now using this.state.notes
    }
  }
});

Hope this is starting to make sense. Highly recommend looking through the Reflux (or Redux, similar but different) examples.



The best approach is to keep all notes in global state and add new entities there one by one when needed. It can be achieved with help of global stores, like Redux or Reflux.

By : xCrZx


This video can help you solving your question :)
By: admin