How to set a hamburger menu using React-Hamburger-Menu? - javascript

I am trying to make a menu that uses CSS3 to fade in and out when you click on the hamburger menu. I am using the react-hamburger-menu.
Although I can't figure out how to use the handleClick function and how to make it that when you click the menu button, or any of the links it triggers the toggle on the className.
I'm using the Gatsby react starter if that matters...
Here's what I have coded so far
import { Link } from "gatsby"
import PropTypes from "prop-types"
import React from "react"
import './Header.css'
import { HamburgerMenu } from "react-hamburger-menu";
handleClick() {
this.setState({
open: !this.state.open
});
}
const Header = ({ siteTitle }) => (
<div className="Header">
<div className="HeaderGroup">
<Link to="/" className="logo"><img src={require('../../images/logo.svg')} width="70" alt="Ryan B. Designs"/></Link>
<HamburgerMenu
isOpen={this.state.open}
menuClicked={this.handleClick.bind(this)}
width={18}
height={15}
strokeWidth={1}
rotate={0}
color='black'
borderRadius={0}
animationDuration={0.5}
/>
</div>
<div className={"MainNavigation " + (this.state.open ? 'show' : 'hidden')}>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About Me</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
</div>
</div>
)
Header.propTypes = {
siteTitle: PropTypes.string,
}
Header.defaultProps = {
siteTitle: ``,
}
export default Header
What I want to happen is when I click the hamburger button, the menu fades in, and when you click the close button or one of the links, the menu fades out using CSS3.

Beside the syntax error #JaromandaX pointed out in the comment, you're using setState in a function component. As it is right now, this doesn't have a setState, it's pointing to the module itself I believe. It should be point to a React class component that has state initiated:
class MyComponent extends React.Component {
// gatsby default environment supports class properties
state = {
isOpen: false,
}
handleClick = () => this.setState({ ... }) // this is now MyComponent
render() {
return (...)
}
}
or you can use useState hook.

Related

Apply inline style to an element in React from a child element

I am still very new to React and I am trying to recreate a navigation bar that was originaly made with HTML, CSS and JS.
I have a Layout component and a Navbar component. In the Navbar component I have a button that when clicked should apply an inline style or a CSS class to the <main>{children}</main> element in the Layout component.
The Navbar element is used inside the Layout element, so I am wondering if this is even possible?
layout.js
import React from "react"
import { useStaticQuery, graphql} from "gatsby"
import Navbar from "./navbar"
const Layout = ({ location, title, children }) => {
const data = useStaticQuery(graphql`
{
site {
siteMetadata {
menuLinks {
link
name
}
}
}
}
`)
return (
<div>
<Navbar pages={ data.site.siteMetadata.menuLinks } />
<main>{children}</main>
</div>
)
}
export default Layout
navbar.js
import React, { useState } from "react"
import { Link } from "gatsby"
import styles from "./styling/navbar.module.less"
const Navbar = ( props ) => {
const [navbarState, setNavbarState] = useState(false);
let toggleNavbar = () => {
console.log("Navbar toggle - Activated")
setNavbarState((navbarState) => !navbarState)
}
window.addEventListener('resize', windowResized)
return (
<nav id={"navigation-bar"}>
<div className={`${styles.navLinks} ${navbarState? styles.navActive:""}`}>
{props.pages.map((page, index) => (
<Link key={page.name} className={`${styles.navLink} ${styles.navLinkHoverEffect} ${navbarState? styles.navAnimate:""}`}
style={{animationDelay: `${index / 7 + 0.5}s`}} to={page.link}>
{page.name}
</Link>
))}
</div>
<div className={`${styles.burger} ${navbarState? styles.toggle:""}`} onClick={toggleNavbar}>
<div className={styles.line1}></div>
<div className={styles.line2}></div>
<div className={styles.line3}></div>
</div>
</nav>
)
}
export default Navbar
Sure it's possible. When you want the child, Navbar, to change something in the parent (normally state) you can pass a function down. In Layout:
const [classes, setClasses] = useState('');
const setClassNames = (classnames) => {
setClasses(classnames)
}
Pass this function down to the child as a prop.
<Navbar pages={ data.site.siteMetadata.menuLinks } setClassNames={setClassNames} />
When the action occurs on the child, say an onClick event then call that function with the classes string you want to set on main.
Then in your parent component, Layout change the line to:
<main className={classes}>{children}</main>
Of note, the way it's currently setup you could just pass the setClasses to Navbar but I used a new function to just to show a more common scenario when you have to customize anything. Also you should use useCallback on the function setClassNames in Layout if you don't want Navbar to re-render every time.
Feel free to leave a comment with questions.

Gatsby open modal window on button click

I'm working on a website about donation using Gatsby v2 (Reactjs) and I need an example like open modal window on button donation click using Gatsby or ReactJS. I search on internet and I got nothing.
Thanks in advance.
Here's a simple (Gatsby) page that makes use of react-modal as an example. In this example I've replaced the default IndexPage in the Gatsby v2's new starter site that you can generate via the CLI.
import React, { Component } from 'react'
import ReactModal from 'react-modal'
import { Link } from 'gatsby'
import Layout from '../components/layout'
ReactModal.setAppElement('#main')
class IndexPage extends Component {
constructor(props) {
super(props)
this.state = {
isModalOpen: false,
}
}
handleModalOpen = event => {
// console.log('handleModalOpen: ', event);
this.setState({ isModalOpen: true })
}
handleModalClose = event => {
// console.log('handleModalOpen: ', event);
this.setState({ isModalOpen: false })
}
render() {
return (
<Layout>
<div id="main">
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<Link to="#" onClick={this.handleModalOpen}>
Donate Now
</Link>
</div>
<ReactModal
isOpen={this.state.isModalOpen}
onRequestClose={this.handleModalClose}
contentLabel="Example Modal In Gatsby"
>
<h2>Donate</h2>
<button onClick={this.handleModalClose}>Close Modal</button>
</ReactModal>
</Layout>
)
}
}
export default IndexPage
That should get you going. Best to read up on how to expand on this example using react-modal here (or use an alternative).
First of all the above example will not work because #main doesn't exist, right now the id for Gatsby app is ___gatsby, so it shoul look like this
ReactModal.setAppElement('#___gatsby')
But it's not good to hope that this id is not going to change in the future. So it's better to implement your own React modal component, here is a good example https://www.digitalocean.com/community/tutorials/react-modal-component

Reactjs: how to update state of parent from child

I am trying to update state of my parent component through child component via setState. below is my parent component:
Fulllayout
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import Header from '../components/header/header.jsx';
import Customizer from '../components/customizer/customizer';
import { Navbar, NavbarBrand, Collapse } from 'reactstrap';
export const settings = {
navbarbg: 'skin1',
sidebarbg: 'skin6',
logobg: 'skin6'
}
class Fulllayout extends React.Component {
constructor(props) {
super(props);
this.state = {
settings: settings
};
}
render() {
return (
<div id="main-wrapper">
<header data-navbarbg={this.state.settings.navbarbg}>
<Navbar expand="md" className={}></Navbar>
</header>
<div className="page-wrapper d-block"></div>
<Customizer />
</div>
);
}
}
export default Fulllayout;
in parent i have defined one constant settings which is exported. It is also given in the this.state. and in header, there is an attribute data-navbarbg={this.state.settings.navbarbg}.
I wanted to change its value dynamically. So, i have one customizer which is imported in parent as a child. Below is the child component:
customizer
import React from 'react';
import { settings } from '../../layouts/fulllayout';
import update from 'immutability-helper';
class Customizer extends React.Component {
constructor(props) {
super(props);
this.navbarbgChange = this.navbarbgChange.bind(this);
this.state = {
settings: settings
};
}
navbarbgChange(e) {
var skin = e.currentTarget.dataset.navbarbg;
var abc = update(this.state.settings, {
navbarbg: { $set: skin }
});
this.setState({ settings: abc });
}
render() {
return (
<aside className="customizer" id="customizer">
<a className="service-panel-toggle text-white"></a>
<div className="customizer-body pt-3">
<div className="mt-3 border-bottom px-3">
<ul className="theme-color mb-2">
<li><a data-navbarbg="skin1" onClick={this.navbarbgChange}></a></li>
</ul>
</div>
</div>
</aside>
);
}
}
export default Customizer;
From customizer, by clicking on color, i wanted to setState of parent to the value given in data-navbarbg attribute.
If i put this code in parent jsx file, it is working fine but for some reasons, this files should be kept separated.
So, what is missing in my code? or the whole approach is wrong? Thanks.
Is there a reason for navbarbgChange to be defined in Customizer?
You could consider moving navbarbgChange to Fulllayout instead.
That way you can do
<li><a data-navbarbg="skin1" onClick={this.props.navbarbgChange}></a></li>
This will ensure that the Fulllayout has the updated background in its state. This also ensures that there is good separation of concerns since settings is defined in the parent and not the child component
In react you can always pass methods from parent to child. Let's write method in the parent to change the state of the parent from the child like this.
class Fulllayout extends React.Component {
constructor(props) {
super(props);
this.state = {
settings: settings
};
}
// This will be passed to the child component
changeForCustomizerState() {
this.setState({
settings: abc
});
}
changeForHeaderState() {
this.setState({
settings: abc
});
}
render() {
return (
<div id="main-wrapper">
<header chageNavbarbg={this.changeForHeaderState}>
<Navbar expand="md" className={}></Navbar>
</header>
<div className="page-wrapper d-block"></div>
<Customizer chageNavbarbg={this.changeForCustomizerState} />
</div>
);
}
}
Then onClick on the child just call the parent method from the child which is passed from the parent.
render() {
return (
<aside className="customizer" id="customizer">
<a className="service-panel-toggle text-white"></a>
<div className="customizer-body pt-3">
<div className="mt-3 border-bottom px-3">
<ul className="theme-color mb-2">
<li><a data-navbarbg="skin1" onClick={this.props.chageNavbarbg}></a></li>
</ul>
</div>
</div>
</aside>
);
}
The state update can be done in Parent from Child using callbacks. Check below code for better understanding
Fulllayout:
updateState = (skin) => {
this.setState({
settings.navbarbg: skin
});
}
render(){
return(
<div>
<Customizer updateState={this.updateState}
</div>
);
}
And in your customizer
navbarbgChange(e){
const skin = e.currentTarget.dataset.navbarbg;
this.props.updateState(skin);
}
OR
Fulllayout:
updateState = (e) => {
const skin = e.currentTarget.dataset.navbarbg;
this.setState({
settings.navbarbg: skin
});
}
render(){
return(
<div>
<Customizer updateState={this.updateState}
</div>
);
}
Customizer: directly pass parent function to onClick
<li><a data-navbarbg="skin1" onClick={this.props.updateState}></a></li>
Also stop using var, and start using let and const mostly. ECMASCRIPT itself argues to avoid using var because of its window scope.

React Bootstrap Checkbox in Nav -> NavItem does not rerender on click

I have a peculiar problem with using a Nav and NavItem with a Checkbox from React Bootstrap. The thing is that if I click directly on the checkbox and not the NavItem button the checkbox will not re-render correctly but my state has updated.
Example: Given the code below I render the component and click directly on the checkbox. In this case showMap will be set to false since we set it to true in the constructor but the checkbox will still be checked in the html view. If I however click on the NavItem but not directly on the checkbox both the state showMap is updated correctly as well as the view.
https://react-bootstrap.github.io/components.html#navs
import * as React from "react";
import * as ReactDOM from "react-dom";
import { LinkContainer } from 'react-router-bootstrap';
import { Col, Nav, NavItem, Checkbox } from "react-bootstrap";
interface IProps {
}
interface IState {
showMap: boolean
}
export class Menu extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
showMap: true
}
}
toggleCheckbox = (event: any) => {
event.preventDefault();
this.setState({ showMap: !this.state.showMap });
}
render() {
return (
<div>
<Nav bsStyle="tabs" activeKey="1">
<LinkContainer to="/test">
<NavItem eventKey="1">Test</NavItem>
</LinkContainer>
<LinkContainer to="/test2">
<NavItem eventKey="2">Test2</NavItem>
</LinkContainer>
<NavItem eventKey="3" onClick={this.toggleCheckbox}><Checkbox name="showMap" inline checked={this.state.showMap} readOnly >
Show Map </Checkbox></NavItem>
</Nav>
</div>
)
}
}
Update:
Tried it like this as well, still the same result:
import * as React from "react";
import * as ReactDOM from "react-dom";
import { LinkContainer } from 'react-router-bootstrap';
import { Navbar, Nav, NavItem, Checkbox } from "react-bootstrap";
interface IProps {
}
interface IState {
showMap: boolean
}
export class Menu extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
showMap: true
}
}
toggleCheckbox = (event: any) => {
event.preventDefault();
this.setState({ showMap: !this.state.showMap });
}
render() {
return (
<div>
<Navbar>
<Nav bsStyle="tabs" activeKey="1">
<LinkContainer to="/test">
<NavItem eventKey="1">Test</NavItem>
</LinkContainer>
<LinkContainer to="/test2">
<NavItem eventKey="2">Test2</NavItem>
</LinkContainer>
<NavItem eventKey="5" onClick={this.toggleCheckbox}><input type="checkbox" name="showMap"
checked={this.state.showMap} readOnly />Show map</NavItem>
</Nav>
</Navbar>
</div>
)
}
}
Created an issue with React Bootstrap here. https://github.com/react-bootstrap/react-bootstrap/issues/2876
They did however think it had to do with React so I created an issue with them https://github.com/facebook/react/issues/11539.
The React team however got to the conclusion this was browser native behavior.
If anyone ends up here this is how I fixed it:
toggleCheckbox = (event: any) => {
this.setState({ showMap: !this.state.showMap });
}
const showMapStyle: React.CSSProperties = {
paddingTop: '15px',
paddingBottom: '15px',
}
...
</Nav>
<div onClick={this.toggleCheckbox} role='button' style={showMapStyle}><input type="checkbox" name="showMap"
checked={this.state.showMap} readOnly />Show map</div>
</Navbar>
I also tried with adding a li item inside <Nav> but then I got this warning:
React does not recognize the activeKey prop on a DOM element. If you
intentionally want it to appear in the DOM as a custom attribute,
spell it as lowercase activekey instead. If you accidentally passed
it from a parent component, remove it from the DOM element.
https://github.com/react-bootstrap/react-bootstrap/issues/2199
<li onClick={this.toggleCheckbox} role='button' style={showMapStyle}><input type="checkbox" id="showMap" name="showMap"
checked={this.state.showMap} readOnly />Show map</li>

ReactJS: Separating Components Best Practices

So, I have a react-bootstrap nav and I want to have one of the nav items open and close a bootstrap modal component.
I have this working with this:
import React, { Component, render } from 'react';
import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Modal, Button } from 'react-bootstrap';
export default class NavigationBar extends Component {
constructor() {
super();
this.state = {
showModal: false
}
}
close() { this.setState({ showModal: false }); }
open() { this.setState({ showModal: true }); }
render() {
return (
<div>
<Navbar>
...entire navbar...
</Navbar>
<Modal show={this.state.showModal} onHide={() => this.close()}>
...entire modal... which would be nice to put if a different file
</Modal>
</div>
); } }
Ideally, I would like to put the modal in a different component file and import it in, but when I do, I'm lost on how to translate the navbar open and close.
What is the best practice for combining components while maintaining their state across files?
Thanks!
A good way to think about it is containers vs presentational components. Containers hold your state and most of your logic. Presentational components take in inputs (props) and render html (jsx) [and do little else].
So, you could make your own Modal component that takes in the methods to call on close and one on whether or not it's shown. It could even be a stateless component - if it's just props + jsx, no need for a full class structure:
import React, { PropTypes } from 'react';
const MyModal = ({ show, onHide}) => (
<Modal show={show} onHide={onHide}>
// ...entire modal...
</Modal>
);
// displayName and propTypes are always good to have
MyModal.displayName = 'MyModal';
MyModal.propTypes = {
show: PropTypes.bool.isRequired,
onHide: PropTypes.func.isRequired,
};
export default MyModal;
then to use it, you will need to make sure to bind your methods so they're called in the right context:
class NavigationBar extends Component {
constructor() {
super();
this.state = {
showModal: false
};
// this is the important binding
this.close = this.close.bind(this);
this.open = this.open.bind(this);
}
close() { this.setState({ showModal: false }); }
open() { this.setState({ showModal: true }); }
render() {
return (
<div>
<Navbar>
// ...entire navbar...
</Navbar>
<MyModal
show={this.state.showModal}
onHide={this.close}
>
// child content if needed (unless it's all defined in MyModal)
</Modal>
</div>
);
}
}
You can wrap your react-bootstrap Modal with your content into your own custom component like so:
import React from 'react';
import { Modal } from 'react-bootstrap';
const NewModal = ({show, onHide}) => {
<Modal show={show} onHide={onHide}>
Modal content in here
</Modal>
};
export default NewModal;
And then import that modal from your component file
import Modal from 'components/modal' // Import your new modal's default export

Categories

Resources