reactjs - Why is react-single-page-navigation only working in Components without constructor and state?
问题描述
I'm currently learning react and I found this cool package: https://www.npmjs.com/package/react-single-page-navigation.
I started using it and everything went well. Then I wanted to make my web app responsive and so I built a mobile navigation (simple dropdown) for small screens.
First, this navigation worked too! The dropdown was always shown and i could klick on the buttons with their references and i got navigated to the wished section through a smooth scrolling. Because of the show-and-hide functionality of the dropdown, I added an constructor with the state menuVisible to the mobile navigation Component and two functions to handle the click on the "open dropdown" button and the click anywhere else but the dropdown items (standard dropdown mechanism).
This mechanism works well and the dropdwon looks good. But: The navigation does not work anymore. Instead, i always get "navigated" some pixels up and down on the first section of my web app.
Here's the working desktop navigation Component:
import React, {Component} from 'react';
export default class DesktopNavigation extends Component {
render() {
return (
<nav className={this.props.sticky ? 'navbar navbar--sticky' : 'navbar'} id='mainNav'>
<div className='navbar--logo-holder'>
<img alt='logo' className='navbar--logo'/>
<h1 className={this.props.sticky ? 'navbar--logo-text-sticky' : 'navbar--logo-text'}>Booyar</h1>
</div>
<ul className='navbar--link'>
<div ref={this.props.refs.Home} onClick={() => this.props.goTo('Home')}>
<div className={(this.props.activeElement === 'Home' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Home
</div>
</div>
<div ref={this.props.refs.About} onClick={() => this.props.goTo('About')}>
<div className={(this.props.activeElement === 'About' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
About
</div>
</div>
<div ref={this.props.refs.Services} onClick={() => this.props.goTo('Services')}>
<div className={(this.props.activeElement === 'Services' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Services
</div>
</div>
<div ref={this.props.refs.Projects} onClick={() => this.props.goTo('Projects')}>
<div className={(this.props.activeElement === 'Projects' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Projects
</div>
</div>
<div ref={this.props.refs.Contact} onClick={() => this.props.goTo('Contact')}>
<div className={(this.props.activeElement === 'Contact' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Contact
</div>
</div>
</ul>
</nav>
);
}
}
And here are the not correctly working mobile navigation Components:
import React, {Component} from 'react';
import DropdownMenu from "./dropdownMenu";
export default class MobileNavigation extends Component {
constructor(props) {
super(props);
this.state = {
sticky: this.props.sticky
}
}
callbackIsSticky = (isSticky) => {
this.setState({sticky: isSticky})
}
render() {
return (
<nav className={this.state.sticky || this.props.sticky ? "mobile-navbar mobile-navbar-sticky" : "mobile-navbar"} id="mainNav">
<div className="mobile-navbar--logo-holder">
<img alt="logo" className="mobile-navbar--logo"/>
</div>
<DropdownMenu
refs={this.props.refs}
activeElement={this.props.activeElement}
goTo={this.props.goTo}
sticky={this.props.sticky}
callback={this.callbackIsSticky}
/>
</nav>
);
}
}
import React, {Component} from 'react';
/**
* This class represents the dropdown menu in mobile view.
*/
export default class DropdownMenu extends Component {
constructor(props) {
super(props);
this.state = {
menuVisible: false
}
this.handleClick = this.handleClick.bind(this);
this.handleOutsideClick = this.handleOutsideClick.bind(this);
}
handleClick() {
if (!this.state.menuVisible) {
document.addEventListener('click', this.handleOutsideClick, false);
this.props.callback(true);
} else {
document.removeEventListener('click', this.handleOutsideClick, false);
this.props.callback(false);
}
this.setState(stateBefore => ({
menuVisible: !stateBefore.menuVisible,
}));
}
handleOutsideClick(event) {
if (this.node.contains(event.target)) {
return;
}
this.handleClick();
}
render() {
const items =
<div className={'dropdown-items'}>
<button
ref={this.props.refs.Home} onClick={() => this.props.goTo('Home')}
className={(this.props.activeElement === 'Home' ? "active-mobile-page " : "")}>
Home
</button>
<button
ref={this.props.refs.About} onClick={() => this.props.goTo('About')}
className={(this.props.activeElement === 'About' ? "active-mobile-page " : "")}>
About
</button>
<button
ref={this.props.refs.Services} onClick={() => this.props.goTo('Services')}
className={(this.props.activeElement === 'Services' ? "active-mobile-page " : "")}>
Services
</button>
<button
ref={this.props.refs.Projects} onClick={() => this.props.goTo('Projects')}
className={(this.props.activeElement === 'Projects' ? "active-mobile-page " : "")}>
Projects
</button>
<button
ref={this.props.refs.Contact} onClick={() => this.props.goTo('Contact')}
className={(this.props.activeElement === 'Contact' ? "active-mobile-page " : "")}>
Contact
</button>
</div>;
return (
<div className={'dropdown-menu' + (this.state.menuVisible ? ' dropdown-menu-open' : '')}
ref={node => this.node = node}>
<button className={'hamburger hamburger--elastic' + (this.state.menuVisible ? ' is-active' : '')} onClick={this.handleClick} type={'button'}>
<span className="hamburger-box">
<span className={'hamburger-inner' + (this.state.menuVisible || this.props.sticky ? ' hamburger-inner-sticky' : '')}>
</span>
</span>
</button>
{this.state.menuVisible ? items : undefined}
</div>
);
}
}
Of course, everything is wrapped by <ScrollNavigation elements={{Home: {}, About: {}, Services: {}, Projects: {}, Contact: {}}}>...</ScrollNavigation>
as described in the documentation of react-single-page-navigation.
I decide whether to show mobile or desktop navigation with css and the following class:
import React, {Component} from 'react';
import MobileNavigation from "./mobileNavigation";
import DesktopNavigation from "./desktopNavigation";
export default class Navigation extends Component {
render() {
return (
<div>
<MobileNavigation
refs={this.props.refs}
activeElement={this.props.activeElement}
goTo={this.props.goTo}
sticky={this.props.isSticky}/>
<DesktopNavigation
refs={this.props.refs}
activeElement={this.props.activeElement}
goTo={this.props.goTo}
sticky={this.props.isSticky}/>
</div>
);
}
}
I hope this is enough information. I do not like to post my whole project, but i think this is not necessary. The desktop navigation works as expected. The mobile navigation just navigates millimeters in the Home section. I think there is a basic react "thing" causing that behaviour that I can't figure out, because the mobile nav works when i uncomment everything but the render methods (and the stuff in the render methods that calls the uncommented stuff ;) ).
If you need more information or have further questions, please ask! :)
Thanks in advance and happy holidays!
Maximotus
解决方案
Ok, so my mistake was to put the reference of the scroll navigation ref={this.props.refs.Home}
(etc.) to the buttons. I somehow misunderstood the instructions in the docs of react-single-page-navigation. The ref prop is like the 'anchor' so the scroll navigation knows where to scroll. And I added this ref to the buttons itself, so the navigation just scrolled to the positions of the buttons.
So I just removed it from the buttons and everything works well now (same for desktop navigation, idk why it worked there before with the same mistake).
<ul className='navbar--link'>
<div onClick={() => this.props.goTo('Home')}>
<div
className={(this.props.activeElement === 'Home' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Home
</div>
</div>
<div onClick={() => this.props.goTo('About')}>
<div
className={(this.props.activeElement === 'About' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
About
</div>
</div>
<div onClick={() => this.props.goTo('Services')}>
<div
className={(this.props.activeElement === 'Services' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Services
</div>
</div>
<div onClick={() => this.props.goTo('Projects')}>
<div
className={(this.props.activeElement === 'Projects' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Projects
</div>
</div>
<div onClick={() => this.props.goTo('Contact')}>
<div
className={(this.props.activeElement === 'Contact' ? 'active-page ' : '') + (this.props.sticky ? 'navbar--link-item-sticky' : 'navbar--link-item')}>
Contact
</div>
</div>
</ul>
Now, the triggers for the scroll navigation only define the goTo prop and the ref prop is defined by the sections of my website.
推荐阅读
- svn - 无法摆脱 svn 合并冲突
- django - 使用 Django Rest Framework 显示来自相关模型的数据
- python-3.x - 检查列表列表中的最大值
- python - 我们可以使 `__getattribute__` 成为一个描述符(以任何可用/有意义的方式)吗?
- python - Python - ValueError:无法强制转换为系列,长度必须为 48:给定 8
- amazon-web-services - AWS ELB 返回 502
- wordpress - WP Bootstrap Navwalker 不显示“当前菜单项”类
- android - 为什么单击片段中的下一步按钮时没有显示我的日历视图?
- excel - 如何对选择进行排序
- c++ - 使用非常量对象作为 const 参数