使用React JS显示/隐藏(子)列表项

时间:2016-12-13 04:24:46

标签: javascript css reactjs

在React JS组件中,我使用来自数组的JS map函数呈现项目列表(Recipes),从App父组件传入。每个项目都有一个子列表(成分),再次使用地图功能进行渲染。

当我点击食谱标题时,我想要显示/隐藏成分的子列表。我在标题上使用onClick函数将CSS设置为none或block,但是我收到以下错误:

未捕获的TypeError:无法读取未定义的属性'openRecipe'

这是我的代码:

var App = React.createClass({


  getInitialState(){

    return{

      showModal:false,


      recipeKeys: [ ],

      recipes: [ ]

    }


  },

  addRecipeKey: function(recipe){


    var allKeys = this.state.recipeKeys.slice();

    var allRecipes = this.state.recipes.slice();

    allKeys.push(recipe.name);

    allRecipes.push(recipe);

    localStorage.setObj("recipeKeys", allKeys);


    this.setState({recipeKeys: allKeys, recipes: allRecipes}, function(){

      console.log(this.state);


    });


  },

  componentDidMount: function(){

    var dummyRecipes = [

      {
        "name": "Pizza",

        "ingredients": ["Dough", "Tomato", "Cheese"]

      },

      {
        "name": "Sushi",

        "ingredients": ["Rice", "Seaweed", "Tuna"]

      }

    ]


    if(localStorage.getItem("recipeKeys") === null){

        localStorage.setObj("recipeKeys", ["Pizza", "Sushi"]);

        dummyRecipes.forEach(function(item){

        localStorage.setObj(item.name, item);  

        });    

        this.setState({recipeKeys: ["Pizza", "Sushi"], recipes: dummyRecipes}, function(){

          console.log(this.state);

        });


    } else {

      var recipeKeys = localStorage.getObj("recipeKeys");

      var recipes = [];

      recipeKeys.forEach(function(item){

        var recipeObject = localStorage.getObj(item);

        recipes.push(recipeObject);

     });


     this.setState({recipeKeys: recipeKeys, recipes: recipes}, function(){

       console.log(this.state);

     });



    }


  }, 


  open: function(){


    this.setState({showModal:true});

  },

    close: function(){


    this.setState({showModal:false});

  },

  render: function(){

    return( 

      <div className="container">
        <h1>Recipe Box</h1>
        <RecipeList recipes = {this.state.recipes} />
        <AddRecipeButton openModal = {this.open}/>
        <AddRecipe closeModal = {this.close} showModal={this.state.showModal} addRecipeKey = {this.addRecipeKey}/>
      </div>

    )  

  }


});

var RecipeList = React.createClass({


  openRecipe: function(item){


    var listItem = document.getElementById(item);

    if(listItem.style.display == "none"){

        listItem.style.display = "block";

    } else {

      listItem.style.display = "none";      
    }    

   },

  render: function(){    

   return (

      <ul className="list-group">

        {this.props.recipes.map(

          function(item,index){

            return (

              <li className="list-group-item" onClick={this.openRecipe(item)}> 
                <h4>{item.name}</h4>
                <h5 className="text-center">Ingredients</h5>
                <hr/>
                <ul className="list-group" id={index} >
                {item.ingredients.map(function(item){

                  return (
                    <li className="list-group-item">  
                    <p>{item}</p>
                     </li>  
                  )

                })} 
                </ul>  
              </li>  

            )

          }


        )


        }

      </ul>  


    ) 


  } 


});

ReactDOM.render(<App />, document.getElementById('app'));

另外,我在这里尝试使用CSS方法,但是使用React可能有更好的方法吗?

任何人都可以帮助我吗?谢谢!

2 个答案:

答案 0 :(得分:2)

您的问题是您在地图中丢失了此上下文...您需要将.bind(this)添加到地图功能的末尾

{this.props.recipes.map(function(item,index){...}.bind(this))};

我回答了另一个question very similar to this here。如果你可以使用箭头功能,它会自动为你绑定哪个最好。如果你不能这样做,那么要么使用绑定,要么在地图函数中使用你在这个上下文中使用的阴影变量。

现在进行清理部分。您需要稍微清理一下代码。

var RecipeList = React.createClass({
  getInitialState: function() {
    return {display: []};
  },
  toggleRecipie: function(index){
    var inArray = this.state.display.indexOf(index) !== -1;

    var newState = [];
    if (inArray) { // hiding an item
        newState = this.state.display.filter(function(item){return item !== index});
    } else { // displaying an item
        newState = newState.concat(this.state.display, [index]);
    }
    this.setState({display: newState});
  },
  render: function(){
   return (
      <ul className="list-group">
        {this.props.recipes.map(function(item,index){
            var inArray = this.state.display.indexOf(index) !== -1;
            return (
              <li className="list-group-item" onClick={this.toggleRecipie.bind(this, index)}> 
                <h4>{item.name}</h4>
                <h5 className="text-center">Ingredients</h5>
                <hr/>
                <ul className="list-group" id={index} style={{display: inArray  ? 'block' : 'none'}} >
                {item.ingredients.map(function(item){
                  return (
                    <li className="list-group-item">  
                    <p>{item}</p>
                     </li>  
                  )
                }.bind(this))} 
                </ul>  
              </li>  
            )
          }.bind(this))
        }
      </ul>  
    ) 
  } 
});

这可能有点复杂,您可能不想管理一系列标记以切换成分视图。我建议你为你的代码制作组件,这样它的反应更加集中,它可以更容易地切换视图。

我将用ES6语法编写这个,因为你应该使用ES6。

const RecipieList = (props) => {
    return (
        <ul className="list-group">
            {props.recipes.map( (item,index) => <RecipieItem recipie={item} /> )
        </ul>  
    );
};

class RecipieItem extends React.Component {
    constructor(){
        super();
        this.state = {displayIngredients: false}
    }
    toggleRecipie = () => {
        this.setState({displayIngredients: !this.state.displayIngredients});
    }
    render() {
        return (
            <li className="list-group-item" onClick={this.toggleRecipie}> 
                <h4>{item.name}</h4>
                <h5 className="text-center">Ingredients</h5>
                <hr/>
                <ul className="list-group" style={{display: this.state.displayIngredients  ? 'block' : 'none'}} >
                    {this.props.recipie.ingredients.map( (item) => <IngredientItem ingredient={item} /> )} 
                </ul>  
            </li>
        );
    }
}

const IngredientItem = (props) => {
    return (
        <li className="list-group-item">  
            <p>{props.ingredient}</p>
        </li>
    );
};

答案 1 :(得分:0)

您也可以使用以下内容:

override var highlighted: Bool {
        get {
            return super.highlighted
        }
        set {
            if newValue {
                backgroundColor = UIColor.blackColor()
            }
            else {
                backgroundColor = UIColor.whiteColor()
            }
            super.highlighted = newValue
        }
    }