Reputation: 125
My code works but I feel like there's a way to do this without declaring a ton of state.
When the nav is clicked, it opens all SectionHeaders, and when one of those SectionHeaders is clicked, it opens the SubSections (only one SubSection allowed to be opened at once)
isFilterOpen
Open but subs closed
One sub open (only one at a time, they toggle)
Right now, my code looks like this:
class MobileFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
isFilterOpen: false,
isSectionOpen: {
Business: false,
Resource: false,
Need: false,
Implementation: false,
Type: false,
Foundations: false,
Advantage: false,
Advanced: false,
Catalyst: false,
Team: false,
},
};
this.filterBar = React.createRef();
}
handleFilterClick = () => {
const {
isFilterOpen
} = this.state;
this.setState({
isFilterOpen: !isFilterOpen,
});
};
handleSectionClick = title => {
let selectedSection = title;
if (title.split(' ').length > 1) {
selectedSection = title.split(' ')[0]; // eslint-disable-line
}
this.setState(prevState => {
const newState = {};
Object.keys(prevState.isSectionOpen).forEach(key => {
newState[key] = false;
});
newState[selectedSection] = !prevState.isSectionOpen[selectedSection];
return {
...prevState,
isSectionOpen: {
...newState,
},
};
});
};
render() {
const { isFilterOpen } = this.state;
const {
need = '',
implementation = '',
type = '',
customerStoriesURL = '',
vertical,
} = this.props;
const filterClasses = isFilterOpen
? 'showMobileSections'
: 'hideMobileSections';
const wrapperClass = isFilterOpen
? 'mobileFilterWrapperActive'
: 'mobileFilterWrapper';
const filterData = this.getData(vertical);
if (vertical === 'services') {
return (
<div className="filterBarMobile" ref={this.filterBar}>
<div className="mobileFilterWrapperContainer">
<div className={wrapperClass}>
<button
type="button"
onClick={this.handleFilterClick}
className="filterHead"
>
Navigate Hub
</button>
<div className={filterClasses}>
{this.renderSections('Foundations', filterData.Foundations)}
</div>
<div className={filterClasses}>
{this.renderSections('Advantage', filterData.Advantage)}
</div>
<div className={filterClasses}>
{this.renderSections('Advanced', filterData.Advanced)}
</div>
<div className={filterClasses}>
{this.renderSections('Catalyst', filterData.Catalyst)}
</div>
<div className={filterClasses}>
{this.renderSections(
'Team Edition',
filterData['Team Edition'],
)}
</div>
</div>
</div>
</div>
);
}
return (
<div className="filterBarMobile" ref={this.filterBar}>
<div className="mobileFilterWrapperContainer">
<div className={wrapperClass}>
<button
type="button"
onClick={this.handleFilterClick}
className="filterHead"
>
Navigate Hub
</button>
<div className={filterClasses}>
{this.renderSections(need, filterData.need)}
</div>
{implementation ? (
<div className={filterClasses}>
{this.renderSections(implementation, filterData.implementation)}
</div>
) : null}
<div className={filterClasses}>
{this.renderSections(type, filterData.type)}
</div>
<div className={filterClasses}>
<div className="sectionTab">
<Link className="sectionLabel" to={customerStoriesURL}>
Customer Stories
</Link>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default MobileFilter;
As you can see, there's way too much state going on -- there as to be a way to make this more founded on the data / props that are coming in and not in a way that requires me listing out all of the SubSections as a nested state.
Any ideas would help. Thanks!
Upvotes: 1
Views: 349
Reputation: 125
i think i've found the solution. i needed to start from scratch. here's what i have:
import React, { Component } from 'react';
import { Link } from 'gatsby';
import Search from '../Search';
import { businessData } from './filterData';
import './newFilter.less';
class NewFilter extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
openSubSection: '',
};
}
handleClick = () => {
const { isOpen } = this.state;
if (!isOpen) {
this.setState({
openSubSection: '',
});
}
this.setState({
isOpen: !isOpen,
});
};
handleSubClick = (e, title) => {
const { openSubSection } = this.state;
if (openSubSection === title) {
this.setState({
openSubSection: '',
});
} else {
this.setState({
openSubSection: title,
});
}
};
// renderLinks = sublevels => sublevels.map(({ title }) => <div>{title}</div>);
renderLinks = sublevels =>
sublevels.map(({ url_slug, title }) => {
if (!url_slug) {
return (
<div className="sectionLabelSub" key={title}>
{title}
</div>
);
}
return (
<Link
className="mobileSubLinks"
key={url_slug}
to={`/${url_slug}/`}
style={{ display: 'block' }}
>
{title}
</Link>
);
});
renderSection = section => {
const { isOpen, openSubSection } = this.state;
const { title, sublevels } = section;
let sectionClass = 'hideMobileSections';
let sectionOpen = 'sectionTabClosed';
let subSectionClass = 'hideMobileContent';
let arrowClass = 'arrow arrow--active';
if (isOpen) {
sectionClass = 'showMobileSections';
}
if (openSubSection === title) {
subSectionClass = 'showMobileContent';
sectionOpen = 'sectionTabOpen';
arrowClass = 'arrow';
}
// const sectionClass = isOpen ? 'section__open' : 'section__closed';
return (
<div className={sectionClass}>
<button
onClick={e => this.handleSubClick(e, title)}
type="button"
key={title}
className={sectionOpen}
>
<button type="button" className="sectionLabel">
{title}
</button>
<div className={arrowClass} />
</button>
<div className={subSectionClass} role="button" tabIndex="0">
{this.renderLinks(sublevels)}
</div>
</div>
);
};
renderSections = sections =>
sections.map(section => this.renderSection(section));
render() {
const { isOpen } = this.state;
const { navTitle, sections } = businessData;
let wrapperClass = 'mobileFilterWrapper';
if (isOpen) {
wrapperClass = 'mobileFilterWrapperActive';
}
return (
<div className="filterBarMobile" ref={this.filterBar}>
<Search vertical='business' />
<div className="mobileFilterWrapperContainer">
<div className={wrapperClass}>
<button
onClick={() => this.handleClick()}
type="button"
className="filterHead"
>
{navTitle}
</button>
{this.renderSections(sections)}
</div>
</div>
</div>
);
}
}
export default NewFilter;
basically i let the data inform the components, pass in the title to the button and the click event, and then the class looks to see if the title from the data matches the title (string) attached to the state
Upvotes: 1