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
var displayed = this.updateDisplay()