Hide/Show elements in React - javascript

So I have this code wherein there's a button in the Header.jsx file that once clicked, it will display the content of the Notification.jsx file. The problem here is, I don't exactly know how can I display the content of Notification.jsx in index.js. I tried using conditional statements but to no avail and is it possible to hide the h1 element once the button is clicked?
Header.jsx
import React from "react";
import { Button } from "#mui/material";
import { IconButton } from "#mui/material";
import NotificationsIcon from "#mui/icons-material/Notifications";
import SearchIcon from "#mui/icons-material/Search";
import Menu from "#mui/material/Menu";
import MenuItem from "#mui/material/MenuItem";
import FullNotifList from "./FullNotifList"
export default function Header() {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<Button
id="basic-button"
aria-controls={open ? "basic-menu" : undefined}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
onClick={handleClick}
>
<IconButton>
<NotificationsIcon />
</IconButton>
</Button>
<Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
"aria-labelledby": "basic-button",
}}
>
{/* Button needs to be clicked in order to display Notification.jsx */}
<Button variant="contained">Notification Center</Button>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
<IconButton>
<SearchIcon />
</IconButton>
</div>
);
}
Notification.jsx
import React from "react";
export default function Notification(){
return(
<div>
<ul>
<li> Hello </li>
<li> Hello </li>
<li> Hello </li>
<li> Hello </li>
</ul>
</div>
)
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
import Header from './Header'
import './index.css'
import Footer from './Footer';
import Notification from './Notification';
export default function Page(props) {
const [isClicked, setIsClicked] = React.useState(false)
function showHide(e) {
setIsClicked(true)
};
return(
<div className='main'>
<Header onClick={showHide}/>
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
)
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
Here is the sandbox link: https://codesandbox.io/s/friendly-darkness-9u5s22?file=/src/index.js

You were right to use conditional rendering, however your conditional logic isn't working because you're assuming your Header component has an event listener.
Events listeners are an important part of Javascripts history, but are more or less abstracted out with library's like Material UI. More on that in a minute, first let me present to you the quick and easy solution; in your index.js, utilize event listeners with vanilla html:
<div onClick={showHide}>
<Header />
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
You'll also want to use a boolean switch in cases like these:
function showHide(e) {
setIsClicked(!isClicked);
}
To listen to events, earlier versions used attachEvent:
element.attachEvent('onclick', function() { /* do stuff here*/ });
Modern browsers now support useEventListener:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
Here you can attach event listeners to as many elements as you want (memory permitting), including the users window element. That final parameter is a relic of pre-universal Javascript browser support, so now capture events and bubbling events are both supported. The default (false) is set to bubbling, where events 'bubble up' the DOM tree from the target node. More information on that here.
You can target your event listeners in Header.jsx to handle your events manually, and learn a little about JS event propagation. Since this is React, we want to utilize the useEffect, and useCallback hook to stop infinite rendering. Then well pass the callback function as props, so you can 'interact' with the Header component from index.js:
<div className="main">
<Header callback={showHide} />
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
then in Header. jsx:
import { useCallback, useEffect } from "react";
export default function Header(props) {
const handleClick = useCallback((event) => {
props.callback(event);
}, [props]);
useEffect(() => {
window.addEventListener("click", handleClick);
}, [handleClick]);
...
Note that the window element will target the whole component. To add events to individual html elements with id="header-component", use:
let element = document.getElementById("header-component");
Now for the third and I'd say best solution, utilizing React's props design pattern. Header.jsx:
export default function Header(props) {
return(
<Button onClick={props.callback}>Display Notifications</Button> //here we use the MUI button, but you can also use div or any other event listening element
...
);
}
and same thing again in index.js:
<div className="main">
<Header callback={showHide} />
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
In this case, you can use different callback functions to attach as many events as you want to one element, while relying on React to do the heavy lifting.

Related

Reactjs dropdown menu and reusable components

I am following a tutorial to make a dropdown menu but I am stuck in the middle because instead of using the "props" keyword like The instructor did in the tutorial. I passed the props directly as arguments without props dot. When I wanted to implement {open && children} without props I got the Error that children are not defined
This is the tutorial I am following, And the minute when I get the Error https://youtu.be/IF6k0uZuypA?t=543
This is the NavItem.js component where I have the problem
import React, { useState, useEffect } from 'react';
function NavItem({ icon, title, ...props }) {
const [opened, setOpened] = useState(false);
return (
<li className='nav-item'>
<a
href='#'
target=''
className='icon-button'
onClick={() => setOpened(!open)}
>
{icon}
<div>{title}</div>
</a>
{open && children}
</li>
);
}
export default NavItem;
This is the Navbar.js Component where I call NavItem.js component
import React from 'react';
import NavItem from './NavItem';
import BellIcon from './icons/bell.svg';
import { ReactComponent as MessengerIcon } from './icons/messenger.svg';
import { ReactComponent as CaretIcon } from './icons/caret.svg';
import { ReactComponent as PlusIcon } from './icons/plus.svg';
import { ReactComponent as ChevronIcon } from './icons/chevron.svg';
function Navbar() {
return (
<>
<nav className='h-20 bg-[#242526] py-0 px-4 border-b border-[#474a4d] '>
<ul className='max-w-full h-full flex justify-end'>
<NavItem icon={<PlusIcon />} />
<NavItem icon={<BellIcon />} />
<NavItem icon={<MessengerIcon />} />
<NavItem icon={<CaretIcon />}>
{/* Here where to render The dropdown menu Items */}
</NavItem>
</ul>
</nav>
</>
);
}
export default Navbar;
I am using Tailwindcss for styling
Thank you for your help
You must either continue destruct props object:
({ icon, title, children, ...props })
Or access via props
{opened && props.children}

Reactstrap Collapse not working within React component

I've been trying to get my React component to work with the Collapse but I cannot seem to get my component to collapse correctly. Right now it will collapse temporarily when the div is clicked, but it will automatically reopen and not actually collapse any of the information needed. This component is taking multiple "modules" and turning them into their own cards. I've tried using a button instead of a div for the "onClick" and have tried with and without the reactstrap Card and CardBody components.
I'm thinking that the useState hook is somehow getting lost with my other props? Any help would be appreciated.
import React, { useState } from "react";
import { Container, Collapse, Card, CardBody } from "reactstrap";
import ReplayCard from "./ReplayCard";
import AttachmentCard from "./AttachmentCard";
const ModuleCard = (props) => {
const module = props.cardID;
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
<div className="moduleCard">
<div onClick={toggle}>
<h2>{module.m_title}</h2>
<h5>Replay Date: {module.m_date}</h5>
</div>
<Collapse isOpen={isOpen}>
<h5>
{module.m_description}
</h5>
<h3>{module.m_title} Video(s)</h3>
<Container className="ReplayCard" fluid={true}>
{module.m_replay &&
module.m_replay.map((value, index) => {
return (
<ReplayCard
key={index}
cardID={index}
video={value.video}
text={value.text}
/>
);
})}
</Container>
<h3>{module.m_title} Link(s)</h3>
<Container className="AttachmentCard" fluid={true}>
{module.m_attachment &&
module.m_attachment.map((value, index) => {
return (
<AttachmentCard
key={index}
cardID={index}
text={value.text}
link={value.link}
/>
);
})}
</Container>
</Collapse>
</div>
);
};
export default ModuleCard;
The useState does seem to be changing from true to false when a console.log is inserted to the togged but still isn't actually triggering any changes.

filtering out onClick from a mapped array

I am trying to figure out how to filter out a mapped array and making the rest of the results disappear in the same component. I've done the same with React Router as I can route the result to a different page but I am wondering if there is a way to do the same on the same component? I have a Directory component (below) that is mapping through an array to display results of items on the page.
I would like to click on one of the elements and remove the rest. I tried to incorporate a filter method in the same component but drawing blanks on how I should implement it. Let me know what you think!
import React from 'react'
import { Card, CardImg} from 'reactstrap'
function Presentational({example, onClick}){
return(
<Card onClick={()=> onClick(example.name) }>
<CardImg src={example.image}/>
</Card>
)
}
function Directory(props){
const examples = props.propExample.map(example=>{
return (
<div>
<Presentational example={example} onClick={props.onClick} />
</div>
)
})
return(
<div>
{examples}
</div>
)
}
export default Directory;
You may use useState hook for selection
We store clicked elements inside the state variable selected. using useState hook.
When the user clicks on the element react component will remember which element he clicked and will render an array from 1 clicked element [selected].
In order to cleanup selection, just call setSelected()
It is the same logic as you want.
import React, {useState} from 'react'
import { Card, CardImg} from 'reactstrap'
function Presentational({example, onClick}){
return(
<Card onClick={()=> onClick(example.name) }>
<CardImg src={example.image}/>
</Card>
)
}
function Directory(props){
const [selected, setSelected] = useState()
const examples = (selected ? [selected] : props.propExample).map(example=>{
return (
<div>
<Presentational example={example} onClick={(name) => {
props.onClick(name)
setSelected(example)
}}
/>
</div>
)
})
return(
<div>
{examples}
</div>
)
}
export default Directory;
if you want to do it with a filter clause it will look almost the same, but with the extra operations
import React, {useState} from 'react'
import { Card, CardImg} from 'reactstrap'
function Presentational({example, onClick}){
return(
<Card onClick={()=> onClick(example.name) }>
<CardImg src={example.image}/>
</Card>
)
}
function Directory(props){
const [selected, setSelected] = useState()
const examples = props.propExample.filter(it => typeof selected === 'undefined' || it.name === selected).map(example=>{
return (
<div>
<Presentational example={example} onClick={(name) => {
props.onClick(name)
setSelected(name)
}}
/>
</div>
)
})
return(
<div>
{examples}
</div>
)
}
export default Directory;

Passing refs from Component to Component in React

So I am not sure if passing refs would be the best thing to do but it's kinda what I have set-out to do tell me if there is a better option..
So I am trying to have an onClick of a nav link, scroll down to the the div "contactForm".
App.js
import ContactForm from './components/ContactForm'
import ParllaxPage from './components/ParllaxPage'
import NavigationBar from './components/NavigationBar'
import React from 'react';
import './App.css';
const App = () => {
return (
< div cssClass="App" >
<body>
<span><NavigationBar /></span>
<ParllaxPage cssClass="parallax-wrapper" />
<ParllaxPage cssClass="parallax-wrapper parallax-pageOne" />
<ContactForm />
</body >
</div >
);
}
export default App;
I was trying to use forwardRef but I am not sure that I was doing it correctly so...
NavigationBar.js
import ContactForm from "./ContactForm";
import React, { useRef } from "react";
import App from "../App";
import { Nav, Navbar, Form, FormControl, Button } from "react-bootstrap";
const ContactFormRef = React.forwardRef((props, ref) => (
<ContactForm className="contactForm" ref={ref}>
{props.children}
</ContactForm>
));
const scrollToRef = (ref) => ref.current.scrollIntoView({ behavior: "smooth" });
const NavigationBar = () => {
const ref = React.forwardRef(ContactFormRef);
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">A1 Gutters</Navbar.Brand>
<Navbar.Toggle aria-controls="b casic-navbar-nav" />
<Nav className="mr-auto">
<Nav.Link href="#home">Home</Nav.Link>
<Nav.Link href="#link">Link</Nav.Link>
<Nav.Link href="#" onClick={console.log(ref)}>
Contact
</Nav.Link>
</Nav>
</Navbar>
);
};
export default NavigationBar;
I don't think the other files really need to be shown, I am just trying to get the className out of the ContactForm component so I can scroll to it onClick.. I currently just have a console.log in the onClick.
Using Hooks will simplify here.
Have state variable for gotoContact and ref for contactRef
Add click handler for navigation link contact
Add useEffect hook and when ever use click on contact and ref is available (value in ref.current) then call the scroll to view)
import ContactForm from "./components/ContactForm";
import ParllaxPage from "./components/ParllaxPage";
import React, { useState, useEffect, useRef } from "react";
import "./App.css";
const NavigationBar = ({ onClickContact }) => {
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">A1 Gutters</Navbar.Brand>
<Navbar.Toggle aria-controls="b casic-navbar-nav" />
<Nav className="mr-auto">
<Nav.Link href="#home">Home</Nav.Link>
<Nav.Link href="#link">Link</Nav.Link>
<Nav.Link href="#" onClick={() => onClickContact()}>
Contact
</Nav.Link>
</Nav>
</Navbar>
);
};
const App = () => {
const [gotoContact, setGotoContact] = useState(false);
const contactRef = useRef(null);
useEffect(() => {
if (gotoContact && contactRef.current) {
contactRef.current.scrollIntoView();
setGotoContact(false);
}
}, [gotoContact, contactRef.current]);
return (
<div cssClass="App">
<body>
<span>
<NavigationBar onClickContact={() => setGotoContact(true)} />
</span>
<ParllaxPage cssClass="parallax-wrapper" />
<ParllaxPage cssClass="parallax-wrapper parallax-pageOne" />
<div ref={contactRef}>
<ContactForm />
</div>
</body>
</div>
);
};
export default App;
You should identify the div "contactForm" with an id and have an anchor tag point to it:
<div id="contactForm"></div>
You can add scroll-behaviour: smooth to the body in CSS
No need to create a separate ContactFormRef wrapper. Simply use React.forwardRef in ContactForm itself. Those not passing a ref will not have to know it forwards refs.
Then, remember to further pass the ref received to a native element or use useImperativeHandle hook to add methods to it without passing it further down.
const ref = React.forwardRef(ContactFormRef)
This is wrong.
You should do it the same as with native components:
const ref = useRef()
return <ContactForm ref={ref} >
// etc
</ContactForm>
You are not rendering the ContactFormRef, so the reference points no nothing!
App.js should be like:
...
const App = () => {
const myNestedRefRef=React.useRef();
return (
...
<NavigationBar contactRef={myNestedRefRef}/>
...
<ContactForm ref={myNestedRefRef} />
...
);
}
...
ContactForm.js
...
function ContactForm=React.forwardRef((props, ref) => (
<form ref={ref}>
...
</form>
));
NavigationBar.js
const NavigationBar = ({contactRef}) => {
return (
...
<Nav.Link href="#" onClick={console.log(contactRef)}>
...
);
};
Consider that
If the <ContactForm/> hasn't been rendered yet, the ref will look like {current:null}

react button onClick redirect page

I am working on a web application using React and bootstrap. When it comes to applying button onClick, I'm having a hard time to have page being redirect to another. If after a href, I cannot go the another page.
So would you please tell me is there any need for using react-navigation or other to navigate the page using Button onClick ?
import React, { Component } from 'react';
import { Button, Card, CardBody, CardGroup, Col, Container, Input, InputGroup, InputGroupAddon, InputGroupText, Row, NavLink } from 'reactstrap';
class LoginLayout extends Component {
render() {
return (
<div className="app flex-row align-items-center">
<Container>
...
<Row>
<Col xs="6">
<Button color="primary" className="px-4">
Login
</Button>
</Col>
<Col xs="6" className="text-right">
<Button color="link" className="px-0">Forgot password?</Button>
</Col>
</Row>
...
</Container>
</div>
);
}
}
update:
React Router v6:
import React from 'react';
import { useNavigate } from "react-router-dom";
function LoginLayout() {
let navigate = useNavigate();
const routeChange = () =>{
let path = `newPath`;
navigate(path);
}
return (
<div className="app flex-row align-items-center">
<Container>
...
<Button color="primary" className="px-4"
onClick={routeChange}
>
Login
</Button>
...
</Container>
</div>
);
}}
React Router v5 with hooks:
import React from 'react';
import { useHistory } from "react-router-dom";
function LoginLayout() {
const history = useHistory();
const routeChange = () =>{
let path = `newPath`;
history.push(path);
}
return (
<div className="app flex-row align-items-center">
<Container>
...
<Row>
<Col xs="6">
<Button color="primary" className="px-4"
onClick={routeChange}
>
Login
</Button>
</Col>
<Col xs="6" className="text-right">
<Button color="link" className="px-0">Forgot password?</Button>
</Col>
</Row>
...
</Container>
</div>
);
}
export default LoginLayout;
with React Router v5:
import { useHistory } from 'react-router-dom';
import { Button, Card, CardBody, CardGroup, Col, Container, Input, InputGroup, InputGroupAddon, InputGroupText, Row, NavLink } from 'reactstrap';
class LoginLayout extends Component {
routeChange=()=> {
let path = `newPath`;
let history = useHistory();
history.push(path);
}
render() {
return (
<div className="app flex-row align-items-center">
<Container>
...
<Row>
<Col xs="6">
<Button color="primary" className="px-4"
onClick={this.routeChange}
>
Login
</Button>
</Col>
<Col xs="6" className="text-right">
<Button color="link" className="px-0">Forgot password?</Button>
</Col>
</Row>
...
</Container>
</div>
);
}
}
export default LoginLayout;
with React Router v4:
import { withRouter } from 'react-router-dom';
import { Button, Card, CardBody, CardGroup, Col, Container, Input, InputGroup, InputGroupAddon, InputGroupText, Row, NavLink } from 'reactstrap';
class LoginLayout extends Component {
constuctor() {
this.routeChange = this.routeChange.bind(this);
}
routeChange() {
let path = `newPath`;
this.props.history.push(path);
}
render() {
return (
<div className="app flex-row align-items-center">
<Container>
...
<Row>
<Col xs="6">
<Button color="primary" className="px-4"
onClick={this.routeChange}
>
Login
</Button>
</Col>
<Col xs="6" className="text-right">
<Button color="link" className="px-0">Forgot password?</Button>
</Col>
</Row>
...
</Container>
</div>
);
}
}
export default withRouter(LoginLayout);
Don't use a button as a link. Instead, use a link styled as a button.
<Link to="/signup" className="btn btn-primary">Sign up</Link>
React Router v5.1.2:
import { useHistory } from 'react-router-dom';
const App = () => {
const history = useHistory()
<i className="icon list arrow left"
onClick={() => {
history.goBack()
}}></i>
}
This can be done very simply, you don't need to use a different function or library for it.
onClick={event => window.location.href='/your-href'}
I was trying to find a way with Redirect but failed. Redirecting onClick is simpler than we think. Just place the following basic JavaScript within your onClick function, no monkey business:
window.location.href="pagelink"
First, import it:
import { useHistory } from 'react-router-dom';
Then, in function or class:
const history = useHistory();
Finally, you put it in the onClick function:
<Button onClick={()=> history.push("/mypage")}>Click me!</Button>
A very simple way to do this is by the following:
onClick={this.fun.bind(this)}
and for the function:
fun() {
this.props.history.push("/Home");
}
finlay you need to import withRouter:
import { withRouter } from 'react-router-dom';
and export it as:
export default withRouter (comp_name);
useHistory() from react-router-dom can fix your problem
import React from 'react';
import { useHistory } from "react-router-dom";
function NavigationDemo() {
const history = useHistory();
const navigateTo = () => history.push('/componentURL');//eg.history.push('/login');
return (
<div>
<button onClick={navigateTo} type="button" />
</div>
);
}
export default NavigationDemo;
If all above methods fails use something like this:
import React, { Component } from 'react';
import { Redirect } from "react-router";
export default class Reedirect extends Component {
state = {
redirect: false
}
redirectHandler = () => {
this.setState({ redirect: true })
this.renderRedirect();
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to='/' />
}
}
render() {
return (
<>
<button onClick={this.redirectHandler}>click me</button>
{this.renderRedirect()}
</>
)
}
}
if you want to redirect to a route on a Click event.
Just do this
In Functional Component
props.history.push('/link')
In Class Component
this.props.history.push('/link')
Example:
<button onClick={()=>{props.history.push('/link')}} >Press</button>
Tested on:
react-router-dom: 5.2.0,
react: 16.12.0
If you already created a class to define the properties of your Button (If you have a button class created already), and you want to call it in another class and link it to another page through a button you created in this new class, just import your "Button" (or the name of your button class) and use the code below:
import React , {useState} from 'react';
import {Button} from '../Button';
function Iworkforbutton() {
const [button] = useState(true);
return (
<div className='button-class'>
{button && <Button onClick={()=> window.location.href='/yourPath'}
I am Button </Button>
</div>
)
}
export default Iworkforbutton
A simple click handler on the button, and setting window.location.hash will do the trick, assuming that your destination is also within the app.
You can listen to the hashchange event on window, parse the URL you get, call this.setState(), and you have your own simple router, no library needed.
class LoginLayout extends Component {
constuctor() {
this.handlePageChange = this.handlePageChange.bind(this);
this.handleRouteChange = this.handleRouteChange.bind(this);
this.state = { page_number: 0 }
}
handlePageChange() {
window.location.hash = "#/my/target/url";
}
handleRouteChange(event) {
const destination = event.newURL;
// check the URL string, or whatever other condition, to determine
// how to set internal state.
if (some_condition) {
this.setState({ page_number: 1 });
}
}
componentDidMount() {
window.addEventListener('hashchange', this.handleRouteChange, false);
}
render() {
// #TODO: check this.state.page_number and render the correct page.
return (
<div className="app flex-row align-items-center">
<Container>
...
<Row>
<Col xs="6">
<Button
color="primary"
className="px-4"
onClick={this.handlePageChange}
>
Login
</Button>
</Col>
<Col xs="6" className="text-right">
<Button color="link" className="px-0">Forgot password </Button>
</Col>
</Row>
...
</Container>
</div>
);
}
}
With React Router v5.1:
import {useHistory} from 'react-router-dom';
import React, {Component} from 'react';
import {Button} from 'reactstrap';
.....
.....
export class yourComponent extends Component {
.....
componentDidMount() {
let history = useHistory;
.......
}
render() {
return(
.....
.....
<Button className="fooBarClass" onClick={() => history.back()}>Back</Button>
)
}
}
I was also having the trouble to route to a different view using navlink.
My implementation was as follows and works perfectly;
<NavLink tag='li'>
<div
onClick={() =>
this.props.history.push('/admin/my- settings')
}
>
<DropdownItem className='nav-item'>
Settings
</DropdownItem>
</div>
</NavLink>
Wrap it with a div, assign the onClick handler to the div. Use the history object to push a new view.
Make sure to import {Link} from "react-router-dom";
And just hyperlink instead of using a function.
import {Link} from "react-router-dom";
<Button>
<Link to="/yourRoute">Route Name</Link>
</Button>

Categories