How to display username on navbar after login in react redux - reactjs

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 :)

Related

The Profile Picture is not visible on the Top Bar

This a blog page using the Fullstack MERN Application. And it should show the uploaded Profile pic of the user when the particular user is Logged in on the Top bar. But in my case it is not showing up. Same thing is happening for the About me Image. But the difference is that the Image in the About me component is visible when the user is logging out and then logging in again.
The code for the Top Bar component is :
import { useContext } from "react";
import { Link } from "react-router-dom";
import { Context } from "../../context/Context";
import "./topbar.css";
export default function TopBar() {
const { user, dispatch } = useContext(Context);
const PF = "http://localhost:5000/images"
// console.log(PF+user.profilePic)
const handleLogout = () => {
dispatch({ type: "LOGOUT" });
};
return (
<div className="top">
<div className="topLeft">
<i className="topIcon fab fa-facebook-square"></i>
<i className="topIcon fab fa-twitter-square"></i>
<i className="topIcon fab fa-pinterest-square"></i>
<i className="topIcon fab fa-instagram-square"></i>
</div>
<div className="topCenter">
<ul className="topList">
<li className="topListItem">
<Link className="link" to="/">
HOME
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/">
ABOUT
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/">
CONTACT
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/write">
WRITE
</Link>
</li>
<li className="topListItem" onClick={handleLogout}>
{user && "LOGOUT"}
</li>
</ul>
</div>
<div className="topRight">
{user ? (
<Link to="/settings">
<img className="topImg" src={PF + user.profilePic} alt="" />
</Link>
) : (
<ul className="topList">
<li className="topListItem">
<Link className="link" to="/login">
LOGIN
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/register">
REGISTER
</Link>
</li>
</ul>
)}
<i className="topSearchIcon fas fa-search"></i>
</div>
</div>
);
}
COde For the Sidebar:
import axios from "axios";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import "./sidebar.css";
import { useContext } from "react";
import { Context } from "../../context/Context";
export default function Sidebar() {
const [cats, setCats] = useState([]);
const { user } = useContext(Context);
const PF = "http://localhost:5000/images/";
useEffect(() => {
const getCats = async () => {
const res = await axios.get("/categories");
setCats(res.data);
};
getCats();
}, []);
return (
<div className="sidebar">
<div className="sidebarItem">
<span className="sidebarTitle">ABOUT ME</span>
{user && <img
src={PF + user.profilePic}
alt=""
/>}
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptate qui
necessitatibus nostrum illum reprehenderit.
</p>
</div>
<div className="sidebarItem">
<span className="sidebarTitle">CATEGORIES</span>
<ul className="sidebarList">
{cats.map((c) => (
<Link to={`/?cat=${c.name}`} className="link">
<li className="sidebarListItem">{c.name}</li>
</Link>
))}
</ul>
</div>
<div className="sidebarItem">
<span className="sidebarTitle">FOLLOW US</span>
<div className="sidebarSocial">
<i className="sidebarIcon fab fa-facebook-square"></i>
<i className="sidebarIcon fab fa-twitter-square"></i>
<i className="sidebarIcon fab fa-pinterest-square"></i>
<i className="sidebarIcon fab fa-instagram-square"></i>
</div>
</div>
</div>
);
}
The Image of the Top Bar
Also when the user is uploading the image a random no. is added in front of the file in the Data base which
I have manually remove to load the image to the server. As it is not able to read it like:
The random numbers in frond of the name of the file
I tried some Stack over flow answer like:TypeError: Cannot read property 'image' of null
This helped me with the Side bar problem but not fully.

NextJS and bootstrap mobile nav

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

Trying to export const to another component in React

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

Offset Top in React Functional Components, getBoundingClientRect not working properly

I'm trying to implement class-swapping site navigation using React Functional components during the scroll screen.
I added a listener to windows and I'm using the getBoundingClientRect method to check the moment my top arrives in a position to change its class.
The code below always returns the Top equal to zero, regardless of the position of my scroll.
Where am I going wrong in this example?
import React, {useEffect, useRef} from "react";
const Navigation = () => {
const inputRef = useRef();
const sections = [{
name: "Portfolio",
url: "#portfolio"
},
{
name: "About",
url: "#about"
},
{
name: "Contact",
url: "#contact"
}];
const navLinks = sections.map((section, index) => {
return (
<li className="nav-item mx-0 mx-lg-1" key={index}>
<a className="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href={section.url}>{section.name}</a>
</li>
)
});
const handleScroll = () => {
let offsetTop = inputRef.current.getBoundingClientRect().top;
console.log('Top ' + offsetTop);
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
});
return (
<nav className="navbar navbar-expand-lg bg-secondary text-uppercase fixed-top" id="mainNav" ref={ inputRef }>
<div className="container">
<a className="navbar-brand js-scroll-trigger" href="#page-top">Start Bootstrap</a>
<button className="navbar-toggler navbar-toggler-right text-uppercase font-weight-bold bg-primary text-white rounded" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i className="fas fa-bars"></i>
</button>
<div className="collapse navbar-collapse" id="navbarResponsive">
<ul className="navbar-nav ml-auto">
{navLinks}
</ul>
</div>
</div>
</nav>
);
};
export default Navigation;
When scrolling the top, right, bottom, left, heigh and width positions are the same.
I expect that the top value changes when scrolling the page.
My code is working now, follow the final version
import React, { useEffect, useRef, useState } from "react";
import { Link, animateScroll as scroll } from "react-scroll";
const Navigation = () => {
const inputRef = useRef();
const [navClass, setNavClass] = useState('');
const sections = [{
name: "Portfolio",
url: "portfolio"
},
{
name: "About",
url: "about"
},
{
name: "Contact",
url: "contact"
}];
const navLinks = sections.map((section, index) => {
return (
<li className="nav-item mx-0 mx-lg-1" key={index}>
<Link
activeClass="active"
to={section.url}
spy={true}
smooth="easeInOutQuart"
offset={-70}
duration={800}
className="nav-link py-3 px-0 px-lg-3 rounded"
href="">
{section.name}
</Link>
</li>
)
});
const scrollToTop = () => {
scroll.scrollToTop();
};
const handleScroll = () => {
let offsetTop = window.pageYOffset;
if ( offsetTop > 100 ){
setNavClass('navbar-shrink');
}else{
setNavClass('');
}
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
});
return (
<nav className={`navbar navbar-expand-lg bg-secondary text-uppercase fixed-top ${navClass}`} id="mainNav" ref={inputRef}>
<div className="container">
<a className="navbar-brand js-scroll-trigger" href="#page-top" onClick={scrollToTop}>Start Bootstrap</a>
<button className="navbar-toggler navbar-toggler-right text-uppercase font-weight-bold bg-primary text-white rounded" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i className="fas fa-bars"></i>
</button>
<div className="collapse navbar-collapse" id="navbarResponsive">
<ul className="navbar-nav ml-auto">
{navLinks}
</ul>
</div>
</div>
</nav>
);
};
export default Navigation;
If your objective is to change the class of your navbar based on the scroll position of your window, it would make more sense to use window.pageYOffset instead. Consider that the navbar would never actually leave its position, so whenever you call .getBoundingClientRect().top, it will always be 0.
const handleScroll = () => {
let offsetTop = window.pageYOffset;
console.log('Top ' + offsetTop);
};
Check out this sandbox on how you can change the appearance of your navbar on scroll: https://codesandbox.io/s/navbar-change-color-onscroll-jepyc

How to change the active class in a navbar in react

Problem:
I am creating a React web application. In there I have created a side navbar like this.
import React, { PureComponent } from "react";
import imagine from "../../../assets/img/sidebar-2.jpg";
class Sidebar extends PureComponent {
constructor(props) {
super(props);
this.state = {
width: window.innerWidth,
activeTabClassName: "tab1"
};
}
// activeRoute(routeName) {
// return this.props.location.pathname.indexOf(routeName) > -1 ? "active" : "";
// }
updateDimensions() {
this.setState({ width: window.innerWidth });
}
componentDidMount() {
this.updateDimensions();
window.addEventListener("resize", this.updateDimensions.bind(this));
}
render() {
const sidebarBackground = {
backgroundImage: "url(" + imagine + ")"
};
return (
<div className="sidebar" data-image={imagine} id="sidebar">
<div className="sidebar-background" style={sidebarBackground} />
<div className="sidebar-wrapper">
<div className="logo">
<a href="/" className="simple-text">
<img
src="../../images/favicon.png"
style={{ maxHeight: "50px" }}
/>
Trafficfine
</a>
</div>
<ul className="nav">
<li className="nav-item active">
<a className="nav-link" href="/admin/dashbord">
<i className="fas fa-tachometer-alt" />
<p>Dashboard</p>
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/admin/officers">
<i className="fas fa-briefcase" />
<p>Officers</p>
</a>
</li>
<li>
<a className="nav-link" href="/admin/offences">
<i className="fas fa-times-circle" />
<p>Offences</p>
</a>
</li>
<li>
<a className="nav-link" href="/admin/drivers">
<i className="fas fa-bus-alt" />
<p>Drivers</p>
</a>
</li>
</ul>
</div>
</div>
);
}
}
export default Sidebar;
I want to change the active class dynamically when a user is clicking on the nav Item and add some styling to the active class. I saw some similar questions have been asked on the stack overflow and I tried those example. But through those, I was unable to achieve that. Can someone help me to solve my problem by modifying
my code? Thank you.
You already have your target class in state, you just need to add it like this
<div className={"sidebar " + this.state.activeTabClassName} data-image={imagine} id="sidebar">
And call this.setState( {activeTabClassName: "newActiveClass"} ) when you want to change it.

Resources