Barry Reeves
Barry Reeves

Reputation: 45

react - rendering multiple components

So I have two components... and I want them to render one after the other... THe code is below

 var Recipe = React.createClass({

  getInitialState: function(){
    return {editing:false},
   {titles: []}
  },

  edit: function (){
    this.setState({editing:true});
  },


  remove: function(){
    this.props.deleteFromBoard(this.props.index)
  },
  save: function (){

    this.props.updateRecipeText(this.refs.newText.value,this.props.index)
        this.setState({editing:false});

  },


    renderNormal: function() {
    return (
      <div className="recipeContainer">
        <div>{this.props.children}</div>
        <button onClick ={this.edit} className ="button-primary">Edit</button>
        <button onClick ={this.remove} className ="button-remove">Remove</button>
</div>

    );
  },
  renderForm: function() {
    return (
      <div className="recipeContainer">
        <textArea ref="newText" defaultValue ={this.props.children}></textArea>
        <button onClick ={this.save} className ="button-danger">Save</button>
</div>

    );
  },






  render: function() {
    if(this.state.editing){
      return this.renderForm();
  }

    else{
      return this.renderNormal();
    }

  }
});

var RecipeTitle = React.createClass({
    getInitialState: function(){
    return {editingTitle:false}
  },
    edit: function (){
    this.setState({editingTitle:true});
  },



  remove: function(){
    this.props.deleteFromBoard(this.props.index)
  },
  save: function (){

   this.props.updateTitle(this.refs.newTextTitle.value,this.props.index)
        this.setState({editingTitle:false});

  },


  renderTitleNormal: function(){
    return(
          <div>   

    <h2>{this.props.children}</h2>
        <button onClick = {this.edit}>Edit</button> 
</div>
      )
  },
      renderTitleForm: function() {
    return (
      <div>   

                <textArea ref="newTextTitle" ></textArea>
        <button onClick ={this.save} className ="button-danger">Save</button>


</div>

    );
  },

    render: function() {

    if(this.state.editingTitle){
      return this.renderTitleForm();


  }  else{
return this.renderTitleNormal();  

  }
    }
});



var Board = React.createClass({
  getInitialState: function () {

    return {
      recipes: [
      ],
   titles: [

   ]
    } 

  },
  add: function(text,title){
        var arr = this.state.recipes;
arr.push(text);
    this.setState({recipes: arr})
    var arrTitle = this.state.titles;
    arrTitle.push("Title");
    this.setState({titles: arrTitle})
  },


  removeRecipe: function (i) {
    var arr = this.state.recipes;
    console.log(arr);
    arr.splice(i,1);
    this.setState({recipes: arr})
  },
    removeTitle: function (i) {
    var arr = this.state.titles;
    arr.splice(i,1);
    this.setState({titles: arr})
  },
  updateRecipe: function (newText, i) {
    var arr = this.state.recipes;
    arr[i]=newText;
        this.setState({recipes: arr})

  },
eachTitle: function (title, i){
return (<div><RecipeTitle key={i} index={i} updateTitleText={this.updateTitle} >{title}</RecipeTitle></div>);                  
      },


  updateTitleText: function (newTitle, i) {
    var arr = this.state.titles;
    arr[i]=newTitle;
        this.setState({titles: arr})

  },

  eachRecipe:

 function (text, i){
return (<div><Recipe key={i} index={i}  updateRecipeText={this.updateRecipe} deleteFromBoard={this.removeRecipe}>{text}</Recipe></div>);                  
      },
  eachTitle:

 function (title, i){
return (<div><RecipeTitle key={i} index={i} updateTitle ={this.updateTitleText} deleteFromBoard={this.removeRecipe}>{title}</RecipeTitle></div>);                  
      },


   render: function () {
  return(
    <div>
      <button onClick={this.add.bind(null,'Default Text')}>Add New</button>
  <div >
    {this.state.titles.map(this.eachTitle)}

    {this.state.recipes.map(this.eachRecipe)}


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


ReactDOM.render(<Board />
, document.getElementById("app"));

Basically its a recipe title followed by a recipe. The problem is once I render a few what happens is that all the titles go together and not with each recipe eg... I want it like this..

Chicken Reicpe
Recipe here

Beef Recipe
Recipe Here

Cococnut Recipe
Recipe here

Instead its like this.

Chicken Recipe
Beef Recipe
Coconut Recipe

Recipe
Recipe
Recipe

How can I render them together? If you need more code or clarification let me know. Any suggestions to improve code? If it's too long to read let me know too. Thanks,

Upvotes: 1

Views: 476

Answers (5)

Lily Ng
Lily Ng

Reputation: 11

I don't know what your data look like. So I suppose the two states data will look like this

repTitles: [{
        text: 'chicken'
    }, {
        text: 'beef'
    }],
repFormulars: [{
        text: 'chicken-rep'
    }, {
        text: 'beef-rep'
    }]

Then in your render function, you can simply loop through it like

render: function() {
    return (
        <div>
            {this.state.repTitles.map((title, idx) => {
                return (
                    <div>
                        <p>{title.text}</p>
                        <p>{this.state.repFormulars[idx].text}</p>
                    </div>
                );
            })}
        </div>
    );
}

It's straightforward so you can get the idea easily. You could add className to the <p> tag, in case you want to customize it using stylesheet.

Upvotes: 1

Shubham Khatri
Shubham Khatri

Reputation: 282110

You can just access the recipes from recipe array by the index, when iterating through the recipe array

render(){
    var recipe = [...this.state.recipe];
    return(
       <div>
           {this.state.recipeTiles.map((title, index) => {
                   return (
                          <div key={index}>
                               <div>{title}</div>
                               <div>{recipe[index]}</div>
                          </div>

                   )
            });
       </div>
    )
}

Upvotes: 0

James Kraus
James Kraus

Reputation: 3478

You'll want to associate each recipe with a recipe title and then render them together. Your render method should sort of look like this:

render(){

  // This gives us a container that has recipe and title properties
  const recipeContainers = this.state.recipes.map( (recipe, index) => {
    const title = this.state.recipeTiles[index]
    return {
      title,
      recipe
    }
  })

  // Now that we have the info bundled together, we can spit it out on the page
  return (
    <div>
      {recipeContainers.map( ({recipe, title}) => (
      <dl>
        <dt>{this.eachTitle(title)}</dt>
        <dd>{this.eachRecipe(recipe)}</dt>
      </dl>
      )
    </div>
  )

}

Upvotes: 0

Tholle
Tholle

Reputation: 112917

Just use a old fashioned loop. Instead of this:

render() {
  return (
    <div>
      {this.state.recipeTiles.map(this.eachTitle)}
      {this.state.recipes.map(this.eachRecipe)}
    </div>
  );
}

You could e.g. do this:

render() {
  const result = [];
  const { recipeTiles, recipes } = this.state;
  for(let i = 0; i < recipeTiles.length; i++) {
    result.push(this.eachTitle(recipeTiles[i]));
    result.push(this.eachRecipe(recipes[i]));
  }
  return (
    <div>
      {result}
    </div>
  );
}

Upvotes: 0

corvid
corvid

Reputation: 11207

You will have to reconcile the two lists. Suppose recipeTiles has a property of recipe_id and recipe has a property of id. You can simply do something like this:

renderRecipes() {
  const { recipes, recipeTiles } = this.props;
  recipes.map(recipe => {
    const tile = recipeTiles.find(({ recipe_id }) => recipe_id === recipe.id);
    return (
      <div>
        <RecipeTile {...tile} />
        <Recipe {...recipe} />
      </div>
    );
  }))
}

Upvotes: 0

Related Questions