Display or conceal (sub) list elements using React JS

Within a React JS component, I am using the map function in JavaScript to render a list of items (Recipes) that is passed in from an App parent component. Each item in the list has a sub list of ingredients that is also rendered using the map function.

My goal is to toggle the visibility of the ingredient sublist when the user clicks on the recipe title. I have implemented an onClick function on the title which toggles the CSS display property between 'none' and 'block'. However, I encounter the following error:

Uncaught TypeError: Cannot read property 'openRecipe' of undefined

Here is the code snippet causing the issue:

var App = React.createClass({
  // Component logic here
});

var RecipeList = React.createClass({
  // Component logic here
});
ReactDOM.render(<App />, document.getElementById('app'));

I am also exploring a CSS-based solution for this functionality, but I'm wondering if there might be a more optimal way to achieve it using React.

If anyone could provide assistance or suggestions, it would be greatly appreciated. Thank you!

Answer №1

It seems like the issue you're facing is related to losing the context in your map function. To resolve this, you should add .bind(this) at the end of your map function.

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

I recently answered a similar question here. Using arrow functions can automatically bind for you, which is preferable. If not, consider using either a bind method or creating a shadow variable for your context within the map function.

Let's discuss cleaning up your code a bit.

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>  
    ) 
  } 
});

If managing a list of indices to toggle a view of ingredients feels complex, consider making components for your code. This approach makes toggling easier and more React-centric.

Here's how you can rewrite it in ES6 syntax since ES6 is recommended:

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>
    );
};

Answer №2

Another option to consider is utilizing the following method:

  display: function(){    
   var current = this;
   return (
      <ul className="recipe-list">
        {this.props.dishes.map(
          function(entry,idx){
            return (
              <li className="list-item" onClick={current.showRecipe(entry)}>.....

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to showcase JSON data while incorporating HTML tags in a React application?

My JSON data contains HTML tags interspersed within it and I have successfully fetched and displayed the data. See the code snippet below: componentDidMount() { this._callCake(); } _renderEachCake = () => { return ( <EachCake ...

Creating a personalized gradient using Tailwind CSS and Daisy UI framework

I’m attempting to incorporate a unique gradient into my next.js application with the help of Tailwind CSS and Daisy UI. Below is the specific code snippet I plan on using: background: linear-gradient(180deg, rgba(192, 192, 192, 0.04) 0%, rgba(201, 180, ...

What is the best way to append something to the textContent property of an HTML tag within a markup page?

While working on resolving an XSS vulnerability in the jqxGrid where the cell content is rendered as HTML, I encountered a scenario like this: <a href="javascript:alert('test');">Hello</a>. To address this issue, I am exploring ways t ...

Ways to eliminate the excess space surrounding an Image border

I am looking to position the image at the top right corner of the screen. body{ font-family: 'Roboto', sans-serif; font-weight: 300; background: #822B2B; color: #ffffff; overflow: hidden; } #showcase{ display: flex; justify-content: center; al ...

Refresh a page in AngularJS with only a single click

I am currently working with angularjs and I am trying to figure out how to refresh the page only once when it loads. Here is what I have attempted so far: <script> app.cp.register('userProfileController', function ($window) { debugger; ...

Angular2 allows you to create pipes that can filter multiple values from JSON data

My program deals with an array of nested json objects that are structured as follows: [{name: {en:'apple',it:'mela'}},{name:{en:'coffee',it:'caffè'}}] I am looking to implement a pipe that can filter out objects b ...

Ways to apply a box shadow exclusively to specific elements

Is there a way to apply a box-shadow to a div without it affecting specific other divs? In simpler terms, is it possible to prevent shadows from being cast on a particular div? Take for example this scenario: http://jsfiddle.net/Cd6fE/ How can I prevent ...

VueJS advisory: Refrain from directly altering a prop

When attempting to modify a prop value using the @click directive, I encountered a warning message: [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed pr ...

Make sure to allow the async task to complete before beginning with Angular JS

As I develop an app using MobileFirst v8 and Ionic v1.3.1, I encounter issues with timing in my code execution. Specifically, when the application initiates, the regular ionic angular code within my app.js file runs. This section of the code handles the i ...

What sets apart `npm install --save-dev gulp-uglify` from `npm install gulp-uglify`?

I'm feeling perplexed regarding the npm installation process. Based on what I've gathered, there are several options available to me when installing modules: The -g option which saves modules globally --save-dev No arguments. Could someone cl ...

What steps should I follow to update my NextJS version from v11 to v13?

I am currently working on upgrading the dependencies of my NextJS project to address security vulnerabilities using npm v9.5.1. The reason behind this task is the detection of a security issue by npm audit in my project: node-fetch <2.6.7 Severity: hi ...

Leveraging .tsx components within nested .tsx components in React Native

Currently, I am delving into the world of building apps using TypeScript in React Native. Coming from a background as a Swift developer, adjusting to JavaScript and TypeScript has been an interesting journey. An observation that stood out to me is the cha ...

Aligning a child div within a parent div using HTML/CSS where the child div takes up 30% of the parent div and

Can anyone help me figure out why these two INPUT elements aren't aligning next to each other? I appreciate any assistance you can offer! <STYLE> html, body { width: 100%; height: 100%; margin: 0px; padding: 0px; } </STYLE& ...

Issue with owl-carousel and Ajax integration

I am experiencing an issue with implementing ajax in my page which contains owl-carousel. Here is the code: Controller: public int Getmembers() { return db.Teams.Count(); } } The View : <script> $(document).ready(funct ...

Enigmatic void found beneath image surfacing

Similar Question: Dealing with Additional Space at the Bottom of an Anchor Tag Take a look at this example page here.. There seems to be a mysterious gap below the picture of the family, just before the gray-bordered (5px #333) div that holds the ima ...

Discover the position of a div relative to another div using ng-drag-drop

I'm currently working on a web application that allows users to create their own identity cards. For this project, I am incorporating the ng-drag-drop library from https://www.npmjs.com/package/ng-drag-drop. The main goal is to obtain the coordinate ...

Which phase is affected by the stopPropagation method?

Quirksmode explains that modern browsers have a capturing phase as well as a bubbling phase, which you can read more about here. If I decide to implement stopPropagation in my event handler with a Boolean argument specifying the phase, how will it behave? ...

When encountering a 404 redirect, CSS and JS files may fail to display

I created a unique error message using .htaccess, incorporating CSS and JS in separate folders. Although it functions properly when accessed directly, integrating these resources into the 404 Error message results in only the HTML text being displayed with ...

Creating fixed and scrollable columns using either Flexbox or Bootstrap

I'm currently facing an issue with creating a responsive webpage layout. My goal is to have a fixed left column that consists of an image taking up 66% of the page, with the remaining 33% dedicated to scrollable content on the right side. To achieve t ...

Is there a method to track the number of active onSnapshot listeners from Firestore in my application?

In my app, I am implementing a feature that dynamically removes query onSnapshot listeners and replaces them with new ones. To ensure that resources are properly freed up, I need to test the effectiveness of the unsubscribe function. Unfortunately, I do n ...