skip to Main Content

I am trying to control the visibility of GalleryItem components by toggling skill components on/off that were used to create those projects. I would like to:
Show ALL if no skills have been toggled
If one or more skills are toggled, show only GalleryItems that use that skill

The selected skills are stored in state.portfolioTypes of Portfolio component – this works as expected.

I simply do not know how to call the updateDisplay method on each of my GalleryItem components from the click handler of my Skill components.

If someone can help me do that I am flying! Thanks.

I have tried pushing my GalleryItem components into an array that is in the parent state so that I can iterate over the array when toggling a Skill, but despite seeing the component objects when I log the array to the console, they are not rendered in the UI – instead are rendered numbers 13-24 (not sure why…)

resumeData.skills looks like:

skills: ["Branding", "Design", "UX", "Marketing", "Print", "Javascript", "HTML", "Grunt", "JQuery", "LessCSS", "Sketch", "Photoshop", "Illustrator", "Bootstrap"]

The item passed to GalleryItem class looks like:

{
    imagePath: "images/portfolio/monster.png",
    name: "Monster.com",
    description: "Web design, UI Development and Art Direction",
    modalImagePath: "images/portfolio/lrg/monster.png",
    modalName: "Web design, UI Development and Art Direction",
    modalDescription: "Working for one of the internet's biggest brands, I developed UI for internal incubator projects, components of the global web application and helped with the full UI redesign of the job seeker experience.",
    modalCategories: ["Branding", "Design", "UX", "Photoshop", "Illustrator"],
    url: "http://www.monster.com"
}

My Portfolio class containing Skill class and GalleryItem classes:
(I have removed some code not relevant to this question)

import React, { Component } from 'react';
export default class Portfolio extends Component {
      constructor(props){
          super(props);
          this.state = {
              portfolioTypes: [],
              galleryItems: []
          }
          this.togglePortfolioItems = this.togglePortfolioItems.bind(this);
      }

      togglePortfolioItems(item){
          //render only portfolio items with selected tags
          console.log("togglePortfolioItems", item);
          let portfolioTypes = this.state.portfolioTypes;
          if(!item.isToggleOn){
              portfolioTypes.push(item.type);
          }else{
              portfolioTypes.splice(portfolioTypes.indexOf(item.type), 1);
          }

          this.setState({portfolioTypes: portfolioTypes});

          console.log(this.state.portfolioTypes, portfolioTypes);
      }

      render() {
        let resumeData = this.props.resumeData;
        let togglePortfolioItems = this.togglePortfolioItems;
        let portfolioTypes = this.state.portfolioTypes;
        let galleryItems = this.state.galleryItems;
        return (
            <React.Fragment>
            <section id="portfolio">
                <div className="row">
                  <div className="twelve columns collapsed">
                    <h1>Check Out Some of My Works.</h1>
                    <div className="skillToggles">
                        {resumeData.skills.map((item,index) => (
                            <Skill 
                                skillName={item}
                                togglePortfolioItems={togglePortfolioItems}
                                galleryItems={galleryItems}
                            />
                        ))}
                    </div>
                    {/* portfolio-wrapper */}
                    <div id="portfolio-wrapper" className="bgrid-quarters s-bgrid-thirds cf">
                        {resumeData.portfolio.map((item,index) => (
                            galleryItems.push(<GalleryItem 
                                item={item}
                                index={index}
                                portfolioTypes={portfolioTypes}
                            />)
                        ))}
                    </div> {/* portfolio-wrapper end */}
                  </div> {/* twelve columns end */}
                </div> {/* row End */}
              </section> {/* Portfolio Section End*/}
          </React.Fragment>
        );

        this.setState({galleryItems: galleryItems});
      }
}

class Skill extends Component {
  constructor(props) {
    super(props);
    this.state = {
        isToggleOn: false,
        type: props.skillName.toLowerCase()
    };

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    e.preventDefault();
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));

    this.props.togglePortfolioItems(this.state);

    let galleryItems = this.props.galleryItems;
    //loop through all galleryItems and set the display of each
    galleryItems.map(galleryItem =>(
        console.log(galleryItem);
        //I would like to fire updateDisplay on galleryItem here
    ));
  }

  render() {
    let skillName = this.props.skillName;
    let skillNameId = skillName.toLowerCase();
    return (
        <React.Fragment>
        <a href="" className={"skill "+(this.state.isToggleOn ? 'on' : 'off')} onClick={this.handleClick}>
            {skillName}
        </a> {/* Skill Section End*/}
      </React.Fragment>
    );
  }
}

class GalleryItem extends Component{
    constructor(props) {
        super(props);
        let portfolioTypes = this.props.portfolioTypes;
        var displayed = true;

        this.state = {
            displayed: displayed
        };
    }

    updateDisplay(){
        let portfolioTypes = this.state.portfolioTypes;
        let displayed = false;
        if(portfolioTypes.length === 0){
            displayed = true;
        }else{
            for(var x=0; x<portfolioTypes.length; x++){
                let cat = portfolioTypes[x];
                if(portfolioTypes.indexOf(cat) > -1){
                    displayed = true;
                }
            };
        }

        this.setState({displayed: displayed});
    }

    render() {
        let item = this.props.item;
        let index = this.props.index;

        return (
            <React.Fragment>
            <div className={"columns portfolio-item "+(this.state.displayed ? "" : "hide ")+item.modalCategories.sort().join(" ").toLowerCase()}>
                <div className="item-wrap">
                  <a href={"#modal-0"+index} title={item.name}>
                    <img alt src={item.imagePath} />
                    <div className="overlay">
                      <div className="portfolio-item-meta">
                        <h5>{item.name}</h5>
                        <p>{item.description}</p>
                      </div>
                    </div>
                    <div className="link-icon"><i className="icon-plus" /></div>
                  </a>
                </div>
              </div>
          </React.Fragment>
        );
      }
}

When I toggle a skill, I would like the gallery to update to only display GalleryItems that used the selected skills.

Perhaps you can also suggest improvements to my approach, as there is probably a better/easier/more robust way to achieve this.

2

Answers


  1. Chosen as BEST ANSWER
    import React, { Component } from 'react';
    export default class Portfolio extends Component {
          constructor(props){
              super(props);
              this.state = {
                  portfolioTypes: []
              }
              this.togglePortfolioItems = this.togglePortfolioItems.bind(this);
          }
    
          togglePortfolioItems(item){
              //render only portfolio items with selected tags
              console.log("togglePortfolioItems", item);
              let portfolioTypes = this.state.portfolioTypes;
              if(!item.isToggleOn){
                  portfolioTypes.push(item.type);
              }else{
                  portfolioTypes.splice(portfolioTypes.indexOf(item.type), 1);
              }
    
              this.setState({portfolioTypes: portfolioTypes});
    
              console.log(this.state.portfolioTypes, portfolioTypes);
          }
    
          render() {
            let resumeData = this.props.resumeData;
            let togglePortfolioItems = this.togglePortfolioItems;
            let portfolioTypes = this.state.portfolioTypes;
            return (
                <React.Fragment>
                <section id="portfolio">
                    <div className="row">
                      <div className="twelve columns collapsed">
                        <h1>Check Out Some of My Works.</h1>
                        <div className="skillToggles">
                            {resumeData.skills.map((item,index) => (
                                <Skill 
                                    skillName={item}
                                    key={index}
                                    togglePortfolioItems={togglePortfolioItems}
                                />
                            ))}
                        </div>
                        {/* portfolio-wrapper */}
                        <div id="portfolio-wrapper" className="bgrid-quarters s-bgrid-thirds cf">
                            {resumeData.portfolio.map((item,index) => (
                                <GalleryItem 
                                    item={item}
                                    index={index}
                                    key={index}
                                    portfolioTypes={portfolioTypes}
                                />
                            ))}
                        </div> {/* portfolio-wrapper end */}
                      </div> {/* twelve columns end */}
                    </div> {/* row End */}
                  </section> {/* Portfolio Section End*/}
              </React.Fragment>
            );
          }
    }
    
    class Skill extends Component {
      constructor(props) {
        super(props);
        this.state = {
            isToggleOn: false,
            type: props.skillName
        };
    
        // This binding is necessary to make `this` work in the callback
        this.handleClick = this.handleClick.bind(this);
      }
    
      handleClick(e) {
        e.preventDefault();
        this.setState(state => ({
          isToggleOn: !state.isToggleOn
        }));
    
        this.props.togglePortfolioItems(this.state);
      }
    
      render() {
        let skillName = this.props.skillName;
        return (
            <React.Fragment>
            <a href="#" className={"skill "+(this.state.isToggleOn ? 'on' : 'off')} onClick={this.handleClick}>
                {skillName}
            </a> {/* Skill Section End*/}
          </React.Fragment>
        );
      }
    }
    
    class GalleryItem extends Component{
        constructor(props) {
            super(props);
            let portfolioTypes = this.props.portfolioTypes;
        }
    
        updateDisplay(){
            console.log("updateDisplay");
            let portfolioTypes = this.props.portfolioTypes;
            let item = this.props.item;
            let displayed = false;
            if(portfolioTypes.length === 0){
                displayed = true;
            }else{
                for(var x=0; x<portfolioTypes.length; x++){
                    let cat = portfolioTypes[x];
                    if(item.modalCategories.indexOf(cat) > -1){
                        displayed = true;
                    }
                };
            }
    
            return displayed;
        }
    
        render() {
            let item = this.props.item;
            let index = this.props.index;
            var displayed = this.updateDisplay();
    
            return (
                <React.Fragment>
                <div className={"columns portfolio-item "+(displayed ? "" : "hide ")+item.modalCategories.sort().join(" ")}>
                    <div className="item-wrap">
                      <a href={"#modal-0"+index} title={item.name}>
                        <img alt="Gallery Image" src={item.imagePath} />
                        <div className="overlay">
                          <div className="portfolio-item-meta">
                            <h5>{item.name}</h5>
                            <p>{item.description}</p>
                          </div>
                        </div>
                        <div className="link-icon"><i className="icon-plus" /></div>
                      </a>
                    </div>
                  </div>
              </React.Fragment>
            );
          }
    }
    

    1. change updateDisplay function like this
    updateDisplay(){
            let portfolioTypes = this.props.portfolioTypes;
            let displayed = false;
            if(portfolioTypes.length === 0){
                displayed = true;
            }else{
                for(var x=0; x<portfolioTypes.length; x++){
                    let cat = portfolioTypes[x];
                    if(portfolioTypes.indexOf(cat) > -1){
                        displayed = true;
                    }
                };
            }
    
            return displayed;
        }
    
    1. Then Define a variable inside render
      var displayed = this.updateDisplay()
    2. use this variable instead of this.state.displayed
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search