Recipe Box - on render, need to populate input field with variable value

How do I get a div (in this case, the div called “overlay2”) to render with an input field pre-populated with a value from an array (such as the title of the recipe from the reclist array)?

For example, when I click on the first View/Edit button for the “spaghetti” recipe and the overlay displays, I want the first input field to be populated with the word “Spaghetti.” The user can either leave it the way it is or type over it.

This was easy to do in Javascript, but for some reason I cannot figure out what to put in the HTML that React is injecting. For example, I tried "{this.state.reclist[i][0]} " in line 96 inside the input tag, but that did not work.

(To see the CodePen for this, go here)

var GenerateRecipesFromList= React.createClass({
    getInitialState: function(){
      const defaultData = [["Spaghetti", "pasta, oil, sauce, parsely, cheese"], ["PB&J", "PB, J"]]
      const localData = JSON.parse(localStorage.getItem('reclist'));
      return {
        reclist: localData ? localData : defaultData
      }
  
    },

  overlayAdd: function() {
      var el = document.getElementById("overlay");
    el.style.visibility = (el.style.visibility === "visible") ? "hidden" : "visible";
  },

  overlayEdit: function() {
      var el2 = document.getElementById("overlay2");
    el2.style.visibility = (el2.style.visibility === "visible") ? "hidden" : "visible";
  },



    exposeAddRecipe: function(){
      var exposeCurrentData = [];
      var userInput = [];
      exposeCurrentData = JSON.parse(localStorage.getItem('reclist'));
      var newTitle = document.getElementById("title").value;
      var newIngredients = document.getElementById("ingredients").value;
  
      userInput.push(newTitle);
      userInput.push(newIngredients);
      exposeCurrentData.push(userInput);
      localStorage.setItem('reclist', JSON.stringify(exposeCurrentData));
      this.setState({ reclist: exposeCurrentData});
      this.overlayAdd();
    },

   exposeEditRecipe: function(sniffer){
    var exposeCurrentData2 = [];
    exposeCurrentData2 = JSON.parse(localStorage.getItem('reclist'));
    var newTitle2 = document.getElementById("title2").value;
      var newIngredients2 = document.getElementById("ingredients2").value;
      exposeCurrentData2[sniffer][0]=newTitle2;
      exposeCurrentData2[sniffer][1]=newIngredients2;
     localStorage.setItem('reclist', JSON.stringify(exposeCurrentData2));
    this.setState({ reclist: exposeCurrentData2})
    this.overlayEdit();
    },

  exposeDeleteRecipe: function(sniffer){
    var exposeCurrentData3 = [];
    exposeCurrentData3 = JSON.parse(localStorage.getItem('reclist'));
    var currentDescription2 = exposeCurrentData3[sniffer];
    exposeCurrentData3.splice(sniffer, 1);
    localStorage.setItem('reclist', JSON.stringify(exposeCurrentData3));
    this.setState({ reclist: exposeCurrentData3});
    },

    render: function(){
      var testData = JSON.parse(localStorage.getItem('reclist'));
      if(testData === null){
      localStorage.setItem('reclist', JSON.stringify(this.state.reclist));
      }
      var currentData = JSON.parse(localStorage.getItem('reclist'));
      var rows = [];
      for(var i=0; i<currentData.length; i++){
        var recipetitle = currentData[i][0];
        var recipeingredients = currentData[i][1];
        var thedivname = i;
          rows.push(<div id= {this.thedivname} className="individual"> <span>{this.state.reclist[i][0]}</span> <button type="button" id="btnEdit" className="normalBtn" onClick={this.overlayEdit}>View/Edit Recipe</button> <button type="button" id="btnEdit" className="normalBtn" onClick={this.exposeDeleteRecipe.bind(this,thedivname)}>Delete Recipe</button>
        </div>);
         }
      return(
        <div>{rows}
        <button type="button" id="btnAdd" onClick={this.overlayAdd}>Add a New Recipe</button>
      
      
          <div id="overlay">
            <div>
              <form > 
                <p>Add a new recipe.</p>
                Recipe Title: <input type="text" name="title" id="title" /><br/>
                Ingredients: <input type="text" name="ingredients" id="ingredients" /><br/>
              <button type="button" className="normalBtn" onClick={this.exposeAddRecipe}>Save</button>
              </form>
              <p>Click here to <a href='#' onClick={this.overlayAdd}>close</a></p>
            
            </div>
          </div>
      
      <div id="overlay2">
        <div>
          <form > 
            <p>Edit an existing recipe.</p>
            Recipe Title: <input type="text" name="title2" id="title2">{this.recipetitle}</input><br/>
            Ingredients: <input type="text" name="ingredients2" id="ingredients2"/><br/>
          <button type="button" onClick={this.exposeEditRecipe.bind(this,thedivname)}>Save Changes</button>
          </form>
          <p>Click here to <a href='#' onClick={this.overlayEdit}>close</a></p>
            
        </div>
      </div>
      
      
      </div>
    
    
   );    
  }
});

var Footer = React.createClass({
    render() {
      return (
    <footer>
      <div id="containerfooter">
           <p>Written by <a href="http://codepen.io/profaneVoodoo/full/dXBJzN/">John Gillespie</a> for FreeCodeCamp   Campers. Happy Coding!</p>
        </div>
      </footer>
    );
  }
 });

 var DisplayRecipes = React.createClass({
  render:function(){
     return(
      <div className = "mainDiv">
           <div className="titleDiv">
          <h1>Recipe Box</h1>
             <GenerateRecipesFromList />
           <Footer />
         </div>        
       </div>
     );
   }
 });

 ReactDOM.render(
   <DisplayRecipes />,
    document.getElementById('Recipes')
  );

You can’t use {this.state.reclist[i][0]} because there is no i (it’s scope is for loop).

Right now I can’t think of an elegant way to implement the functionality (I tried a hacky way and it sort of almost worked, but it’s not the way to do it). The main problem is that you made one giant component and now it is hard to change anything without breaking everything else. Maybe I’ll try again tomorrow (it’s already late here). Or maybe someone more advanced will chime in.

In the meanwhile I would recommend you to watch this React Tutorial (I think the video nr.9 is what you need, but watch all):

It shows how to build a simple ToDo app (and the RecipeBox project basically is a ToDo app);

1 Like

OK, I watched the videos and re-did all the code. i think my application look and runs better, BUT I am still stuck on getting a form just to populate with a default variable, just once on initial render. What am I missing? (please see same CodePen as above with the new code)

I haven’t studied your code in detail but if you want to populate an input field you need to set it by value like this

Recipe Title: <input type="text" name="title2" id="title2" value={this.state.recipetitle} />

but looking at your component there is no recipetitle set in your state anyway so you need to create that before referencing it

1 Like

Handling forms in React is… involved. This post is a great, in-depth look at how to do it:

And he doesn’t bring it Redux or anything else complicated. It’s just straight React.

I got a very similar response from Chris at StackOverflow as well. He suggested

Recipe Title: <input type="text" name="title2" id="title2" value={this.recipetitle}>{this.state.defaultData[0][0]}</input>

However, this actually breaks the application and nothing renders at all. What am I missing?

CORRECTION: That is working! Error was somewhere between chair and keyboard…

Here is the working code

var GenerateRecipesFromList= React.createClass({
getInitialState: function(){
  const defaultData = [["Spaghetti", "pasta, oil, sauce, parsely, cheese"], ["PB&J", "PB, J"]]
  const localData = JSON.parse(localStorage.getItem('reclist'));
  return {
    reclist: localData ? localData : defaultData,
    recepi: ''
   }
   },  

 updateRecList: function (reclist) {
   this.setState({ reclist: reclist });
 },

overlayEdit: function(value) {
  this.setState({recepi: value})
    var el2 = document.getElementById("overlay2");
  el2.style.visibility = (el2.style.visibility === "visible") ? "hidden" : "visible";
 },



  render: function(){
   var testData = JSON.parse(localStorage.getItem('reclist'));
   if(testData === null){
     localStorage.setItem('reclist', JSON.stringify(this.state.reclist));
     }
    var currentData = JSON.parse(localStorage.getItem('reclist'));
    var rows = [];

      for(var i=0; i<currentData.length; i++){
     var thedivname = i;
     var recipetitle=currentData[i][0];
        console.log(recipetitle);
       rows.push(<div id= {this.thedivname} className="individual" onClick={this.overlayEdit.bind(this, recipetitle)}>  <span><h2>{this.state.reclist[i][0]}</h2></span> 
     </div>);
     }
   return(
    <div className="centerMe" >
      <AddButton updateRecList={ this.updateRecList } />
      {rows}

      <div id="overlay2"  >
        <div>
          <form > 
            <p>Edit an existing recipe.</p>
            Recipe Title: <input type="text" name="title2" id="title2" value={this.state.recepi}/>
            <br/>
            Ingredients: <input type="text" name="ingredients2" id="ingredients2"/><br/>
          </form>
          <p>Click here to <a href='#' onClick={this.overlayEdit}>close</a></p>

        </div>
      </div> 

    </div>
   );
   },
  });

  var AddButton = React.createClass({

  overlayAdd: function() {
    var el = document.getElementById("overlay");
  el.style.visibility = (el.style.visibility === "visible") ? "hidden" : "visible";
  },

  exposeAddRecipe: function(){
      var exposeCurrentData = [];
      var userInput = [];
      exposeCurrentData = JSON.parse(localStorage.getItem('reclist'));
     var newTitle = document.getElementById("title").value;
     var newIngredients = document.getElementById("ingredients").value;

     userInput.push(newTitle);
     userInput.push(newIngredients);
     exposeCurrentData.push(userInput);
      localStorage.setItem('reclist', JSON.stringify(exposeCurrentData));
      //this.setState({ reclist: exposeCurrentData});
    this.props.updateRecList(exposeCurrentData);
     this.overlayAdd();
  },

     render: function(){
     return(
     <div>
      <button type="button" id="btnAdd" onClick={this.overlayAdd}><h2>Add a New Recipe</h2></button> 
        <div id="overlay">
         <div>
            <form > 
             <p>Add a new recipe.</p>
             Recipe Title: <input type="text" name="title" id="title">

              </input><br/>
             Ingredients: <input type="text" name="ingredients" id="ingredients" /> <br/>
           <button type="button" className="normalBtn" onClick={this.exposeAddRecipe}>Save</button>
           </form>
           <p>Click here to <a href='#' onClick={this.overlayAdd}>close</a></p>

          </div>
        </div> 
      </div>
      );
     }
  });

  var Footer = React.createClass({
  render() {
  return (
   <footer>
    <div id="containerfooter">
      <p>Written by <a href="http://codepen.io/profaneVoodoo/full/dXBJzN/">John Gillespie</a> for FreeCodeCamp Campers. Happy Coding!</p>
      </div>
    </footer>
   );
  }
 });


var MyApp = React.createClass({  
  render: function() {
  return(
  <div className = "mainDiv">
      <div className="titleDiv">
    <h1>Recipe Box</h1>

       <GenerateRecipesFromList />
       <Footer />
      </div>        
        </div>
    );
  }, 
}); 

ReactDOM.render(
  <MyApp />,
  document.getElementById('Recipes')
);