Good afternoon,
I am having trouble changing the state of components which are siblings. Basically they look like this:
<Navigation>
<NavEndpt /> About <--- no
<NavEndpt /> Blog
<NavEndpt /> Projects
</Navigation>
Each <NavEndpt />
is a link that routes to a different 'page' or route on my website unless it is the first one listed, "About". About I want to be a drop down/lightbox feature something pretty to show off my CSS knowledge. So when one of the links is clicked I want it to go where I want; additionally, I'd like it to reflect which link is currently "active" (or not) by both changing it's state.active
to true
or false
and adding an additional class active
for CSS purposes. I want to new "active" class to only be present on a <NavEndpt />
that's state.active
is true
.
Yet everything I have tried so far hasn't worked and I've been at this for two days. I'd appreciate someone who is more experienced with React to show me how to accomplish this.
Here is what I am working with:
var MasterLayout = React.createClass({
mixins: [History],
render: function(){
var childrenWithProps = React.Children.map(this.props.children,
(child) => React.cloneElement(child, {
doSomething: this.doSomething
})
);
return(
<div id="container">
<Navigation activeRoute={this.props.location.pathname} />
<div id="content">
{childrenWithProps}
</div>
</div>
)
}
});
var Navigation = React.createClass({
getInitialState: function(){
var endpoints = require("./data/naviEnd.js");
return {
endpoints: endpoints,
activeRoute: this.props.activeRoute
}
},
renderEndpoints: function(key){
var endpointDetails = this.state.endpoints[key];
return(
<NavEndpt id={endpointDetails.id} key={endpointDetails.title} url={endpointDetails.url} title={endpointDetails.title}/>
)
},
render: function(){
return(
<div id="navigation">
{Object.keys(this.state.endpoints).map(this.renderEndpoints)}
</div>
)
}
});
// Created child not a this.props.child of <Navigation /> component
// as pointed out by @BenHare
var NavEndpt = React.createClass({
handleClick: function(){
this.setState({
active: true
})
},
render: function(){
return (
<div onClick={this.handleClick} className="navLink" id={this.props.id}>
<Link id={this.props.id + "-link"} to={this.props.url}>{this.props.title}</Link>
</div>
)
}
})
Currently this only changes creates and set states for each <NavEndpt />
I tried to make this mess as clean as possible for Stack Overflow.
The best fix I have come up with so far uses a lot of DOM selection and hardcoded if/else statements. It also doesn't light up my "About" component because it doesn't have a url
property. That's significant because I have the below solution tied up to the pathname
of my entire layout component.
var MasterLayout = React.createClass({
mixins: [History],
render: function(){
var childrenWithProps = React.Children.map(this.props.children,
(child) => React.cloneElement(child, {
doSomething: this.doSomething
})
);
return(
<div id="container">
<Navigation activeRoute={this.props.location.pathname} />
<div id="content">
{childrenWithProps}
</div>
</div>
)
}
});
// This is the parent component that sits on the side or the top depending on the
// broswer size, contains components NavEndpt
var Navigation = React.createClass({
getInitialState: function(){
var endpoints = require("./data/naviEnd.js");
return {
endpoints: endpoints,
activeRoute: this.props.activeRoute
}
},
// Always makes the website's initial view the home route
componentDidMount: function(){
var cover = document.getElementById("cover");
var projects = document.getElementById("projects");
var about = document.getElementById("about");
var active = this.props.activeRoute
this.setActive();
},
// resets the hard coded CSS class
resetClasses: function(){
var active = this.props.activeRoute
var cover = document.getElementById("cover");
var projects = document.getElementById("projects");
var about = document.getElementById("about");
cover.className = "navLink";
projects.className = "navLink";
about.className = "navLink";
},
// checks pathname of <MasterLayout/>
// also somehow makes it so a refresh does not
// return you to "/"
setActive: function(){
var active = this.props.activeRoute
var cover = document.getElementById("cover");
var projects = document.getElementById("projects");
var about = document.getElementById("about");
if (active === "/"){
cover.className += " active";
} else if (active === "/projects"){
projects.className += " active"
} else if (active === "/about"){
about.className += " active"
}
},
// listens for updates, resets active first and sets it
componentDidUpdate: function(){
this.resetClasses();
this.setActive();
},
renderEndpoints: function(key){
var endpointDetails = this.state.endpoints[key];
return(
<NavEndpt id={endpointDetails.id} key={endpointDetails.title} url={endpointDetails.url} title={endpointDetails.title}/>
)
},
render: function(){
return(
<div id="navigation">
{Object.keys(this.state.endpoints).map(this.renderEndpoints)}
</div>
)
}
});
var NavEndpt = React.createClass({
handleClick: function(){
this.setState({
active: true
})
},
render: function(){
return (
<div onClick={this.handleClick} className="navLink" id={this.props.id}>
<Link id={this.props.id + "-link"} to={this.props.url}>{this.props.title}</Link>
</div>
)
}
})