I'm trying to use the addItem const from my Products component and use it in the Header component.
I tried exporting it to the Header component but I must be doing something wrong. The addItem works fine in the tag within my return statement in my Products component. Cannot get addItem to show in the anchor tag in Header component. Hope that makes sense :)
import React, {useState} from "react";
function Products(props)
{
const [addItem, setAddItem] = useState(0);
function increaseCart(event){
setAddItem(addItem + 1);
}
return(
<div class="col-md-4">
<div className ="products-container">
<h1>{addItem}</h1>
<img className="product-image" src ={props.image}></img>
<div class="product-info">
<h6>{props.product}</h6>
<p className="product-price">{props.price}</p>
<p>{props.description.substring(0,200)+"..."}</p>
<div className="buttons-container">
<button type="button" className="btn btn-success btn-lg add-cart-btn product-btn" onClick={increaseCart}>Add to Cart</button>
<button type="button" className="btn btn-success btn-lg more-details-btn product-btn">More details</button>
</div>
</div>
</div>
</div>
)
}
export default Products;
export {addItem};
---------------------------------------------------------------------------------------------------------
import React from "react";
import {addItem} from "./Products";
function Header(){
return(
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand" href="#"><img className="logo" src="images/vr-logo.svg"></img></a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item active">
<a className="nav-link" href="#">VR Headsets<span className="sr-only">(current)</span></a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Graphics Cards</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Games</a>
</li>
<li className="nav-item">
<img className ="nav-link cart" src ="images/supermarket.svg"></img>
</li>
<li className="nav-item">
<a className="nav-link" href="#">{addItem}</a>
</li>
</ul>
</div>
</nav>
)
}
export default Header;
Unfortunately this is something you can't export. In order to accomplish sharing properties from other components you would need to have it passed from the top down and/or back up again, or rely on a central state management system.
What you would need to do:
- App Component: [add, setAdd]
- Header: passed from App
- Products: passed from App
This is sometimes referred to as "Prop Drilling" which is to send props from the top level and pass it through each component that needs it. If you use a state management system, you could avoid this and could use something like (in order of complexity) React Context, Recoil (new), MobX, or Redux.
--
Click the "Run code snippet" below to see it in action:
const { useState } = React;
const Header = props => {
return (<header style={{ lineHeight: '50px', display: 'block', width: 'calc(100% - 20px)', padding: '0 10px', height: '50px', background: '#efefef'}}>My App - Cart: {props.add || 0}</header>)
};
const Product = props => {
const onClickButton = () => {
props.setAdd(props.add + 1);
}
return (<div><h1>My Product</h1><button onClick={onClickButton}>Add</button></div>);
}
const App = () => {
const [add, setAdd] = useState(0);
return (<div>
<Header add={add} />
<Product add={add} setAdd={setAdd} />
</div>);
}
ReactDOM.render(<App />, document.querySelector('#root'));
body {
font-family: Arial, sans-serif;
}
<body>
<div id="root"></div>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script type="text/babel" src="main.js"></script>
</body>
The react way of doing this is to pass your addItem state to your Header component as a prop. That way React can know about in order to reflect the change in your state properly.
The easiest way of doing this is by declaring your addItem state on top of the React component tree, and then pass it as a prop to other components that may use it.
for example, say you have this component structure:
<Header/>
<Cart/>
<Products>
etc...
You can declare your state in the parent component that is holding your component structure so you can pass it as a prop as needed to other components.
<Header/>
<Cart addItem = { addItem }/>
<Products addItem = { addItem } setItem = { setItem }/>
You can also make your state globally accessible with React context API
Or by using a state management library like Redux
Related
There is a nav link on my navbar named Login (I have used bootstrap navbar). After successful login I want to replace Login with a username using react-redux (not redux-toolkit).
Kindly help me in writing reducer and action code. As I am a beginner in redux and I don't know how to code in reducer and action file it is very confusing for me.
Navbar component (Navbar.js)
import React from 'react';
import { Link } from 'react-router-dom';
import { useSelector } from 'react-redux';
const Navbar = () => {
const {username} = useSelector((state) => state.user);
return (
<div>
<nav className="navbar navbar-expand-lg">
<div className="container-fluid">
<Link className="navbar-brand" to="/hommepage">Ebuy</Link>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria- expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon">
<i className="fas fa-bars" style={{color: 'white', fontSize: '28px'}}></i>
</span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
<li className="nav-item">
<Link className="nav-link" aria-current="page" to="/homepage">Home</Link>
</li>
<li className="nav-item dropdown">
<Link className="nav-link dropdown-toggle" to="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Select Category
</Link>
<ul className="dropdown-menu" aria-labelledby="navbarDropdown">
<li><Link className="dropdown-item" to="/skincare">Skincare Products</Link></li>
<li><Link className="dropdown-item" to="/stationary">Stationary Products</Link></li>
<li><Link className="dropdown-item" to="#">Clothing</Link></li>
</ul>
</li>
</ul>
<div className="d-flex">
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
<li className='nav-item'>
<Link className="nav-link" to="/">Login</Link>
</li>
</ul>
</div>
</div>
</div>
</nav>
</div>
);
};
export default Navbar;
reducer
const initialState = {
username: null,
}
export const userReducer(state = initialState, action) {
switch (action.type) {
case "CHANGE_USERNAME":
return {
...state,
username: action.payload,
}
default:
return state
}
}
action
export const changeUserName = (name) => {
return {
type: "CHANGE_USERNAME",
payload: name
}
}
after login call this
const dispatch = useDispatch()
const login = () => {
... login logic here
dispatch(changeUserName("user-1"))
}
in navbar
const { username } = useSelector(state=> state.user)
In your Navbar Component you should get username from redux, please try this :
const {username} = useSelector(state=> state.yourReducerName)
after this, in your jsx : you should write a condition like this :
{username ? (<div>{username}</div>): ()<span>Login</span>}
EDIT :
Please refer to this sandbox example and take a look on the reducer and actions files, also on the navbar components.
Let us know if the post helped you :)
I'm using bootstrap with NextJS for my navbar but have one issue because of the dynamic page changes with React and Next on mobile view the menu stays open after clicking a link and loading new page.
Tried a few ways to try and make it close on page changes but been unsuccessful. What would be best way to achieve this?
import { useEffect } from 'react'
import Link from 'next/link'
import Image from 'next/image'
import { ArrowDownCircle } from 'react-bootstrap-icons'
import logo from '../public/images/logo.gif'
import styles from '../styles/Navbar.module.scss'
const Navbar = () => {
useEffect(() => {
import('bootstrap/js/dist/collapse')
import('bootstrap/js/dist/dropdown')
}, []);
return (
<nav id="navbar" className="navbar navbar-expand-md navbar-light container">
<div>
<Link href="/" prefetch={false}><a className="navbar-brand">
<Image
src={logo}
alt="Logo"
layout="intrinsic"
width={200}
height={50}
priority={true}
/>
</a></Link>
</div>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className={`${styles.navLink} navbar-nav ms-auto`}>
<li className="nav-item">
<Link href="/" prefetch={false}><a className={`${styles.link} nav-link`} aria-current="page">Home</a></Link>
</li>
<li className="nav-item">
<Link href="/about" prefetch={false}><a className={`${styles.link} nav-link`}>About</a></Link>
</li>
<li className="nav-item dropdown">
<a className={`${styles.link} nav-link`} id="navbarDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">Services <ArrowDownCircle className="ms-1" /></a>
<ul className="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<li><Link href="/web-design"><a className="dropdown-item">Web Design</a></Link></li>
<li><Link href="/web-development"><a className="dropdown-item">Web Development</a></Link></li>
<li><Link href="/ecommerce"><a className="dropdown-item">eCommerce</a></Link></li>
</ul>
</li>
<li className="nav-item">
<Link href="/faq" prefetch={false}><a className={`${styles.link} nav-link`}>FAQ</a></Link>
</li>
<li className="nav-item">
<Link href="/contact" prefetch={false}><a className={`${styles.link} nav-link`}>Contact</a></Link>
</li>
</ul>
</div>
<style jsx>{`
a {
background: none;
}
a:hover {
background: none;
}
`}</style>
</nav>
);
}
export default Navbar;
If anyone still looking for solution in 2022 or later, follow these steps:
Note: This solution was tested on NextJs (Typescript) + Bootstrap v.5.2
Add an id to the button with class navbar-toggler i.e. <button id="navbar-toggler" className="navbar-toggler" ...
In your component class, say Header.tsx
Import useRouter and useEffect:
import { useRouter } from 'next/router'
import { useEffect } from 'react'
and within your component function:
const Header = () => {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url: String) => {
if (typeof window !== undefined && window.screen.width <= 991.98) { // change the width value according to your navbar breakpoint
const navbar = document.getElementById("navbar-toggler");
if (navbar !== null && !navbar.classList.contains('collapsed')) navbar.click()
}
}
router.events.on('routeChangeComplete', handleRouteChange)
}, [router.events])
return (<> ... other codes </>)
}
export default Header
I am a total newbie on react. I am trying to use a functional app and wanted to use the function toggleBurgerMenu(). The problem, I am not sure what else I need to define it. Should I use props.toggleBurgerMenu()?
import React from 'react'
import { Link } from "react-router-dom";
import './Header.scss'
const Header = (props) => {
toggleBurgerMenu = () => {
document.querySelector('.navbar-menu').classList.toggle('is-active');
}
return (
<div>
<header>
<nav className="navbar" role="navigation" aria-label="main navigation">
<div id="navbarBasicExample" className="navbar-menu">
<div className="navbar-start">
<Link to="/home" className="navbar-item" onClick={()=> this.toggleBurgerMenu()}>Home</Link>
</div>
</div>
</nav>
</header>
</div>
)
}
export default Header
Appreciate any help. thank you
You need to define your toggleBurgerMenu function like this const toggleBurgerMenu = () => { document.querySelector('.navbar-menu').classList.toggle('is-active'); }. And then you can use it in the onClick Event like this <Link to="/home" className="navbar-item" onClick={toggleBurgerMenu}>Home</Link>
with react the best thing to do is not using document you could use a ref instead, but in this case you can add some state and update the classname
here is an easy example
const Header = (props) => {
/** function to set the state of the button */
const [isActive, setIsActive] = useState(initialState)
return (
<div>
<header>
<nav className="navbar" role="navigation" aria-label="main navigation">
{/**if its true we add is-active class there are several package to this*/}
<div id="navbarBasicExample" className={`navbar-menu ${isActive ? 'is-active' : ''}`}>
<div className="navbar-start">
<Link to="/home" className="navbar-item" onClick={() => setIsActive(prev => !prev.setIsActive)}>Home</Link>
</div>
</div>
</nav>
</header>
</div>
)
}
export default Header
The problem: a Materialize CSS floating button works perfectly, but when I change page (with react router dom) for example HOME > ABOUT > HOME again it doesn't anymore.
Home:
import AddBtn from "../layout/AddBtn-float";
const Home = () => {
return (
<Fragment>
<SearchBar />
<div className="container">
<AddBtn />
...
AddBtn.js
const AddBtn = () => {
return (
<div className="fixed-action-btn direction-top">
<a
href="#add-task-modal"
className="btn-floating btn-large blue darken-2 modal-trigger"
>
<i className="large material-icons">add</i>
</a>
<ul>
<li>
<a
href="#user-list-modal"
className="btn-floating green modal-trigger"
>
<i className="material-icons">person</i>
</a>
</li>
<li>
<a href="#add-user-modal" className="btn-floating red modal-trigger">
<i className="material-icons">person_add</i>
</a>
</li>
</ul>
</div>
);
};
export default AddBtn;
I'm not an expert but looks like a Materialize "bug" that needs a workaround.
Any idea?
Thanks!
I write the solution may someone needs it.
Just put in every page
useEffect(() => {
M.AutoInit();
//eslint-disable-next-line
}, []);
I used it just in App.js but looks not enough
I'm trying to move the delete button's functionality into a modal, so the user should get a confirmation before deleting from a single click. If I open the inspector I can manually make it appear by changing the display:None in CSS, but I thought that's what the Materialize library was handling for me.
When I click on the modal, I see it appearing in the address bar, so I assume react-router is hijacking the modal
I can probably replace the exact path to match /modal2, but should I be sending it to a new component? Or send it back to the same component with a property set for modal?
```
!jsx
import React from 'react';
import { Switch, Route} from 'react-router-dom';
import Barrels from './Barrels';
import About from './About';
import BarrelDetails from './BarrelDetails';
import AddBarrel from './AddBarrel';
import BarrelEdit from './BarrelEdit';
const Main = () => (
<main className="green">
<Switch>
<Route exact path= '/' component={Barrels} />
<Route exact path= '/about' component={About} />
<Route exact path= '/barrels/add' component={AddBarrel} />
<Route exact path= '/barrels/edit/:id' component={BarrelEdit} />
<Route exact path= '#modal2' component={BarrelEdit //modal component propperty turned on?// } />
<Route exact path= '/barrels/:id' component={BarrelDetails} />
</Switch>
</main>
)
export default Main;
```
live demo on Heroku
Repository on BitBucket
or should I be trying to move the modal trigger into the on onDelete function?
```
!jsx
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import logo from '../logo.svg';
class BarrelDetails extends Component {
constructor (props){
super(props);
this.state = {
details: ''
}
}
componentWillMount(){
this.getBarrel();
}
getBarrel(){
let barrelID = this.props.match.params.id;
axios.get(`/api/Barrels/${barrelID}`)
.then (response => {
this.setState({details: response.data}, () =>
{
console.log(this.state);
})
})
.catch(err => console.log(err));
}
onDelete(){
let barrelID = this.state.details.id;
axios.delete(`/api/Barrels/${barrelID}`)
.then ( response => {
this.props.history.push('/');
} ).catch(err => console.log(err));
}
render () {
return (
<div className = "container" >
<header className="App-header z-depth-3">
<h2>{this.state.details.Name}</h2>
<Link className = "btn grey" to = "/">back</Link>
</header>
<ul className = "collection z-depth-3" >
<li className = "collection-item" >planted: <b className = "yellow" > {this.state.details.date_planted}</b> </li>
<li className = "collection-item" >Barrel #: <b className = "yellow" > {this.state.details.barrel_number}</b> </li>
<li className = "collection-item" ><b className = "yellow" > {this.state.details.contents}</b> </li>
<li className = "collection-item" >location: <b className = "yellow" > {this.state.details.location}</b> </li>
<li className = "collection-item" >geolocation: <b className = "yellow" > this.state.details.geoLocaction.toString()</b> </li>
<li className = "collection-item" >notes: <b className = "yellow" > {this.state.details.notes}</b> </li>
<li className = "collection-item" >size: <b className = "yellow" > {this.state.details.size}</b> </li>
<li className = "collection-item" >last checked: <b className = "yellow" > {this.state.details.date_last_checked}</b> </li>
</ul>
<button onClick = {this.onDelete.bind(this) } className = "btn red right"><i className ="far fa-trash-alt"></i> Delete this Barrel</button>
<h5>what that modal do?</h5>
<Link to={`/barrels/edit/${this.state.details.id}`} className="btn waves-effect z-depth-3"><i className = "fas fa-pencil-alt" ></i> Edit this Barrel</Link>
<Link to={`#modal2`} className="btn waves-effect red"><i className ="far fa-trash-alt z-depth-3"></i> Delete this Barrel</Link>
<div id="modal1" className="modal">
<div className="modal-content">
<h4>Modal Header</h4>
<p>A bunch of text</p>
</div>
<div className="modal-footer">
Button
</div>
</div>
<div id="modal2" className="modal orange">
<div className="modal-content">
<h4>Are you sure you want to delete</h4>
<p>A bunch of text</p>
</div>
<div className="modal-footer">
Button
</div>
</div>
<p className="App-intro">
TurtleWolfe.com<br/>
using LoopBack & React<br/>
<img src={logo} className="App-logo" alt="logo" />
</p>
</div>
)
}
}
export default BarrelDetails;
```
<Link to={`#modal2`} className="btn waves-effect red">
<i className ="far fa-trash-alt z-depth-3"></i>
Delete this Barrel
</Link>
This is the part that concerns me. This should not be a <Link> as that is attached to React-Router, you should just be using a button with the styling turned off, then triggering the modal via an onClick event.
You're also trying to show your modal in a way that probably won't work with React. You're going to want to set a local state of dislpayModal: false or something like that, then do a check for that state in your render, instead of relying on Materalize to do it for you. It can be tricky to get DOM based plugins to work in a React environment but doing it with the state is the "React Way" of doing things like this.
Another suggestion for common CSS frameworks with JavaScript pieces built on the DOM is to justo find a 3rd party React based implementation that has already done this for you. Such as: https://react-materialize.github.io/