React update state without refresh - reactjs

I'm trying to update the state in a navigation component to show "Log Out" if a cookie is set. The only problem is I cannot get the cookie without refreshing the entire page. Is there a way for me to get the cookie without a refresh? The below code shows how I'm trying to get the cookie and setting the state.
Updated with code:
import React, {Component} from 'react';
import './NavigationItems.css';
import Logo from '../../Logo/Logo';
import { Route, Link } from 'react-router-dom';
import PortalFindSubscriber from '../../../containers/Portal/PortalFindSubscriber';
import Login from "../../../components/Login/Login";
import Register from "../../../components/Register/Register";
import Reset from "../../../components/Reset/Reset";
import ResetConfirm from "../../../components/Reset/ResetConfirm";
import Aux from '../../../hoc/Aux/Aux';
import Cookies from 'universal-cookie';
const cookies = new Cookies();
class navigationItems extends Component{
constructor() {
super();
this.updateLoggedStatus = this.updateLoggedStatus.bind(this)
}
state = {
isLogged: false,
};
componentDidMount() {
this.setState({isLogged: true});
console.log(this.state.isLogged);
this.updateLoggedStatus();
}
Collapse (e) {
const body = document.body;
const collapseBtn = e.target;
const collapsedClass = "collapsed";
collapseBtn.getAttribute("aria-expanded") === "true"
? collapseBtn.setAttribute("aria-expanded", "false")
: collapseBtn.setAttribute("aria-expanded", "true");
collapseBtn.getAttribute("aria-label") === "collapse menu"
? collapseBtn.setAttribute("aria-label", "expand menu")
: collapseBtn.setAttribute("aria-label", "collapse menu");
body.classList.toggle(collapsedClass);
};
Toggle(e){
const body = document.body;
const toggleMobileMenu = e.target;
toggleMobileMenu.getAttribute("aria-expanded") === "true"
? toggleMobileMenu.setAttribute("aria-expanded", "false")
: toggleMobileMenu.setAttribute("aria-expanded", "true");
toggleMobileMenu.getAttribute("aria-label") === "open menu"
? toggleMobileMenu.setAttribute("aria-label", "close menu")
: toggleMobileMenu.setAttribute("aria-label", "open menu");
body.classList.toggle("mob-menu-opened");
}
onSignOutClick = () =>{
cookies.remove('AccessToken');
this.setState({isLogged: false});
}
updateLoggedStatus() {
this.setState({ isLogged: true });
}
render () {
cookies.addChangeListener(console.log(cookies.get('AccessToken')));
console.log(this.state.isLogged);
//const { isLogged } = this.state;
return(
<Aux>
<header className="pageHeader">
<nav>
<Link to={"/"}>
<Logo />
</Link>
<button className="toggle-mob-menu" aria-expanded="false" aria-label="open menu" onClick={this.Toggle}>
<svg width="20" height="20" aria-hidden="true">
<use xlinkHref="#down"></use>
</svg>
</button>
<ul className="adminMenu">
<li className="menuHeading">
<h3>Admin</h3>
</li>
{cookies.get('AccessToken')!==undefined?
<>
<li>
<Link to={"/"} onClick={this.onSignOutClick}>
<div class="material-icons" style={{marginRight: 10}}>
login
</div>
<span>Log Out</span>
</Link>
</li>
</>
:<li>
<Link to={"/"}>
<svg>
<use xlinkHref="#users"></use>
</svg>
<span>Log In</span>
</Link>
</li>}
<li>
<button className="collapse-btn" aria-expanded="true" aria-label="collapse menu" onClick={this.Collapse}>
<svg aria-hidden="true">
<use xlinkHref="#collapse"></use>
</svg>
<span>Collapse</span>
</button>
</li>
</ul>
</nav>
</header>
<Route exact path="/" component={Login} />
<Route exact path="/terms" component={Register} />
<Route exact path="/reset" component={Reset} />
<Route exact path="/reset_confirm" component={ResetConfirm} />
<Route exact path="/find" component={PortalFindSubscriber} />
</Aux>
);
}
}
export default navigationItems;

You Should use
boolean shouldComponentUpdate(object nextProps, object nextState)
which decided whether the component should be updated or not.
for feasible solution you can use Functional Components's UseEffect callbacks for updating states

Related

If I am using onclick to a button why it redirects me to the same page i am in (reactjs)

how to click on anchor tag in the card and redirects me to another page with more details of the current card example click on opens new tab with current (clicked) card details here is an api for item https://api.npoint.io/d275425a434e02acf2f7/News/0
snippets of code also a link that works https://codesandbox.io/s/sweet-spence-1tl4y5?file=/src/App.js
my api https://api.npoint.io/d275425a434e02acf2f7 for rendering all items in cards
filteredCat?.map((list) => {
if (list.showOnHomepage === "yes") {
const date = format(
new Date(list.publishedDate),
"EEE dd MMM yyyy"
);
const showCat = news.map((getid) => {
if (getid.id == list.categoryID) return getid.name;
});
// const rec = list.publishedDate.sort((date1, date2) => date1 - date2);
return (
<Card
className=" extraCard col-lg-3"
style={{ width: "" }}
id={list.categoryID}
>
<Card.Img
variant="top"
src={list.urlToImage}
alt="Image"
/>
<Card.Body>
<Card.Title className="textTitle">
{list.title}
</Card.Title>
<Card.Text></Card.Text>
<small className="text-muted d-flex">
<FaRegCalendarAlt
className="m-1"
style={{ color: "#0aceff" }}
/>
{date}
</small>
<div
style={{ color: "#0aceff" }}
className="d-flex justify-content-between"
>
<Button variant="" className={classes["btn-cat"]}>
{showCat}
</Button>
<div>
<FaRegHeart />
<FaLink />
</div>
</div>
</Card.Body>
</Card>
);
}
})
}
</div>
}
I tried this technique but it does direct me to the same page not the new tab with empty page !!
function handleClick(event) {
event.preventDefault();
window.location.href = 'src/comp/newsitem';
}
function news() {
return (
<a href="#" onClick={handleClick}>
Click me to redirect!
</a>
);
}
React provides SPAs which means that you can load your content of different pages without any refresh or redirect. So no need to redirect to another page unless you really want to open the page in a new tab.
Also if you want to have multiple page paths, you should use react-router-dom.
So first of all you should add your routes to your app. Add a pages.js file with this content:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import App from './App';
import News from './News';
import NewsItem from './NewsItem';
function Pages() {
return (
<BrowserRouter>
<Routes>
<Route path='/news' element={<News />} />
<Route path='/newsItem' element={<NewsItem />} />
<Route path='/' element={<App />} />
</Routes>
</BrowserRouter>
);
}
export default Pages;
And then import it to your index.js file:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import Pages from "./Pages";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<Pages />
</StrictMode>
);
NewsItem file:
function NewsItem() {
return <div>News Item</div>;
}
export default NewsItem;
And finally when you want to navigate the News page, do this:
import { Link } from 'react-router-dom'
<Link to='/news' />
Or if you want to open in new tab:
<Link to='/news' target='_blank' />
And for navigating to NewsItem page (without any a tag):
<Link to="/newsItem">News Item</Link>

getting "Missing required parameter 'client_id'" error in react-google-login

I inserted my client id correctly and it did work
but suddenly it's not working until now even though I didn't change any codes in <GoogleLogin />
Also it works in blank React when I tested but not working in this project
exact error message is Uncaught qw {message: "Missing required parameter 'client_id'", xM: true, stack: "gapi.auth2.ExternallyVisibleError: Missing require…YZKdHMHzzbYNF1G7KSEIjXaBQQw/cb=gapi.loaded_0:1:15"}
import React, { useState } from "react";
import "./CSS/Header.css";
import { BrowserRouter as Router, Link } from "react-router-dom";
import { GoogleLogin } from "react-google-login";
export default function Header() {
const responseGoogle = (response) => {
console.log(response);
};
return (
<Router>
<div id="header">
<div id="id"></div>
<h2 id="header_title">title</h2>
<div id="contents">
<h4 className="content">
<Link
to="/"
onClick={() => {
window.location.href = "/";
}}
>
HOME
</Link>
</h4>
<h4 className="content">
<Link
to="/history"
onClick={() => {
window.location.href = "/history";
}}
>
History
</Link>
</h4>
<GoogleLogin
clientid=[myclientid]
buttonText="Login"
onSuccess={responseGoogle}
isSignedIn={true}
onFailure={responseGoogle}
cookiePolicy={"single_host_origin"}
/>
</div>
</div>
</Router>
);
}
You should use clientId instead of clientid for the prop

Adding Active class to react sidebar

I am trying to add 'active' as className to my each in sidebar component. What I want is that whichever gets activated, it should attach class 'active' to it and then set CSS accordingly.
I tried using react-router location props referring to this SO answer: https://stackoverflow.com/a/42766792/11349591, but unable to follow the syntax/result properly.
Here is my code for Sidebar.js
import React, {Component} from 'react'
import { NavLink, Link } from 'react-router-dom'
import '../../css/active.css';
export default function SideBar(){
const { location } = this.props;
const dashboardClass = location.pathname === "/" ? "active" : "";
const userClass = location.pathname.match(/^\/user/) ? "active" : "";
const listClass = location.pathname.match(/^\/list/) ? "active" : "";
return (
<div style={{flexBasis: '200px', backgroundColor: 'gray'}}>
<nav style={{textAlign:'left'}}>
<ul className="side-bar">
<li className={dashboardClass}>
<i class="fa fa-pie-chart fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
<Link to="/dashboard">
Dashboard
</Link>
</li>
<li className={userClass}>
<i class="fa fa-user-circle fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
<Link to="/user" >User</Link>
</li>
<li className={listClass}>
<i class="fa fa-list-alt fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
<Link to="/list">Table List</Link>
</li>
</ul>
</nav>
</div>
)
}
App.js (Home component render Sidebar and Dashboard component)
function App() {
return (
<div className="App">
<Switch>
<Route exact path="/" component={Home} />
<Route path="/register" component={Register} />
<Route path="/login" component={Login} />
</Switch>
</div>
);
}
Home.js
export default class Home extends Component {
render() {
return (
<div style={{display: 'flex', flexDirection: 'row'}}>
<SideBar/>
<Dashboard/>
</div>
)
}
}
My console report this problem: TypeError: Cannot read property 'props' of undefined
You are trying to access this.props from a functional component and that's only possible in class based components. Change your code to this:
export default function SideBar({ location }){
// Rest of the code
You also need to pass the router props down from Home to Sidebar component:
export default class Home extends Component {
render() {
return (
<div style={{display: 'flex', flexDirection: 'row'}}>
<SideBar {...this.props}/> // Pass the props down
<Dashboard/>
</div>
)
}
}
To activate the Nav link you should use NavLink instead of Link. NavLink has one property called activeClassName, you should apply that class to it. let say if you have class called active which you want to apply after clicking on that then you can do this.
<NavLink to="/user" activeClassName={classes.active}>
<li className={dashboardClass}>
<i class="fa fa-pie-chart fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
</li>
</NavLink>

Passing props in the Redirect component (React.js) not working as expected

I am working through a practice app to get acquainted with React-Router
In my App component I have a Route which acts as a way to afford for 404's
class App extends Component {
render() {
return (
<Router>
<div>
<Navbar />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/players" component={Players} />
<Route path="/teams" component={Teams} />
{/* params should be last Route as it would match players and teams */}
<Route path="/:teamId" exact component={TeamPage} />
<Route
render={({ location }) => (
<h1 className="text-center">
Sorry, we couldn't find {location.pathname.substr(1)} <br />{" "}
404: page not found.
</h1>
)}
/>
</Switch>
</div>
</Router>
);
}
}
That works fine.
However I have another component TeamPage which essentially has the same mechanism.
import React, { Component } from "react";
import { Redirect, Link } from "react-router-dom";
import { getTeamNames, getTeamsArticles } from "../api";
import TeamLogo from "./TeamLogo";
import Team from "./Team";
import slug from "slug";
export default class TeamPage extends Component {
state = {
loading : true,
teamNames: {},
articles : []
};
componentDidMount() {
Promise.all([
getTeamNames(),
getTeamsArticles(this.props.match.params.teamId)
]).then(([teamNames, articles]) => {
this.setState(() => ({
teamNames,
articles,
loading: false
}));
});
}
render() {
const { loading, teamNames, articles } = this.state;
const { match } = this.props;
const { teamId } = match.params;
if (loading === false && teamNames.includes(teamId) === false) {
return (
<Redirect
to={{
pathname: "/",
location: { location: window.location.pathname.substr(1) }
}}
/>
);
}
return (
<div>
<Team id={teamId}>
{team =>
team === null ? (
<h1>LOADING</h1>
) : (
<div className="panel">
<TeamLogo id={teamId} />
<h1 className="medium-header">{team.name}</h1>
<h4 style={{ margin: 5 }}>
<Link
style = {{ cursor: "pointer" }}
to = {{ pathname: "/players", search: `?teamId=${teamId}` }}
>
View Roster
</Link>
</h4>
<h4>Championships</h4>
<ul className="championships">
{team.championships.map(ship => (
<li key={ship}>{ship}</li>
))}
</ul>
<ul className="info-list row" style={{ width: "100%" }}>
<li>
Established
<div>{team.established}</div>
</li>
<li>
Manager
<div>{team.manager}</div>
</li>
<li>
Coach
<div>{team.coach}</div>
</li>
<li>
Record
<div>
{team.wins}-{team.losses}
</div>
</li>
</ul>
<h2 className="header">Articles</h2>
<ul className="articles">
{articles.map(article => (
<li key={article.id}>
<Link to={`${match.url}/articles/${slug(article.title)}`}>
<h4 className="article-title">{article.title}</h4>
<div className="article-date">
{article.date.toLocaleDateString()}
</div>
</Link>
</li>
))}
</ul>
</div>
)
}
</Team>
</div>
);
}
}
But in this instance I only can accomplish the redirect, with no message presented in the UI.
I am trying to pass props to home or "/" so if someone did that from the TeamPage. The Route in my App component would respond with that message, like it does normally.
Excerpt from TeamPage:
const { loading, teamNames, articles } = this.state;
const { match } = this.props;
const { teamId } = match.params;
if (loading === false && teamNames.includes(teamId) === false) {
return (
<Redirect
to={{
pathname: "/",
location: { location: window.location.pathname.substr(1) }
}}
/>
);
}
Can this be done?
Thanks in advance!
Update Oct 11th 2:56PM
So as per Hannad's great insight I updated my App component and the TeamPage component and created a catch all ErrorPage route. What I need to do now is update the Teams.js file as when I try to go to http://localhost:3000/teams/foo I get the following error:
import React, { Component } from "react";
import { Redirect, Route, Link } from "react-router-dom";
import Sidebar from "./Sidebar";
import { getTeamNames } from "../api";
import TeamLogo from "./TeamLogo";
import Team from "./Team";
export default class Teams extends Component {
state = {
teamNames: [],
loading : true
};
componentDidMount() {
getTeamNames().then(teamNames => {
this.setState(() => ({
loading: false,
teamNames
}));
});
}
render() {
const { loading, teamNames } = this.state;
const { location, match } = this.props;
return (
<div className="container two-column">
<Sidebar
loading = {loading}
title = "Teams"
list = {teamNames}
{...this.props}
/>
{loading === false &&
(location.pathname === "/teams" || location.pathname === "/teams/") ? (
<div className="sidebar-instruction">Select a Team</div>
) : null}
<Route
path = {`${match.url}/:teamId`}
render = {({ match }) => (
<div className="panel">
<Team id={match.params.teamId}>
{team =>
team === null ? (
<h1>Loading</h1>
) : (
<div style={{ width: "100%" }}>
<TeamLogo id={team.id} className="center" />
<h1 className="medium-header">{team.name}</h1>
<ul className="info-list row">
<li>
Established
<div>{team.established}</div>
</li>
<li>
Manager
<div>{team.manager}</div>
</li>
<li>
Coach
<div>{team.coach}</div>
</li>
</ul>
<Link
className = "center btn-main"
to = {`/${match.params.teamId}`}
>
{team.name} Team Page
</Link>
</div>
)
}
</Team>
</div>
)}
/>
</div>
);
}
}
there is an other way to do 404 redirects. which i would suggest. as it has more control..
define your error component which takes simple params.
class App extends Component {
render() {
return (
<Router>
<div>
<Navbar />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/players" component={Players} />
<Route path="/teams" component={Teams} />
{/* params should be last Route as it would match players and teams */}
<Route path="/:teamId" exact component={TeamPage} />
<Route path="/error/:errortype" exact component={ErrorPage} />
</Switch>
</div>
</Router>
);
}
}
// your redirection logic
const { loading, teamNames, articles } = this.state;
const { match } = this.props;
const { teamId } = match.params;
if (loading === false && teamNames.includes(teamId) === false) {
return (
<Redirect
to={'/error/404'}
/>
);
}
// ErrorPage Component
const ErorPage = ({match: { params:{ errortype } }})=>(
// have your logic for different templates for different types of errors.
// ex.
<div>
{
errortype === 404 ?
<div>you got a 404</div> :
<div>sone generic message</div>
// this logic can change with respect to your app.
}
</div>
)

React Router v4 setting activeClass on parent

Not too familiar with react router, but I need the functionality of the NavLink to set the active class on the parent li element, and not the a element.
To implement this I just looked at the source code of the NavLink and copied it to a new element. (Example using typescript, but just about the same as js anyway)
import * as React from 'react';
import { Link, withRouter, Route } from 'react-router-dom';
class LiNavLink extends React.Component<any, {}> {
render() {
const {to,exact, strict, activeClassName, className, activeStyle, style, isActive: getIsActive, ...rest } = this.props;
return (
<Route
path={typeof to === 'object' ? to.pathname : to}
exact={exact}
strict={strict}
children={({ location, match }) => {
const isActive = !!(getIsActive ? getIsActive(match, location) : match)
return (
<li
className={isActive ? [activeClassName, className].join(' ') : className}
style={isActive ? { ...style, ...activeStyle } : style}>
<Link
to={to}
{...rest}
/>
</li>
)
}}
/>
);
}
}
export default LiNavLink;
Then the usage:
<ul>
<LiNavLink activeClassName='active' exact={true} strict to="/example"><span>Active</span></LiNavLink>
<LiNavLink activeClassName='active' exact={true} strict to="/example/archived"><span>Archived</span></LiNavLink>
</ul>
I'm using a HashRouter and for some reason which I can't figure out, this does not update when the route changes, only when I hard 'refresh' the page does it update how it should.
I believe it is never updating because the props never change? So it doesn't know to update itself?
How can I get this to update? Or is my problem somewhere else?
In v4 after lots of tries I did.
Here my working code.
import React, { Component } from "react";
import logo from "../../logo.svg";
import { Link, withRouter } from "react-router-dom";
import PropTypes from "prop-types";
class Navbar extends Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
state = {};
getNavLinkClass = path => {
return this.props.location.pathname === path
? "nav-item active"
: "nav-item";
};
render() {
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<Link className="navbar-brand" to="/">
<img
src={logo}
width="30"
height="30"
className="d-inline-block align-top"
alt=""
/>
Utility
</Link>
<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" />
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className={this.getNavLinkClass("/")}>
<Link className="nav-link" to="/">
Home
</Link>
</li>
<li className={this.getNavLinkClass("/age-counter")}>
<Link className="nav-link" to="/age-counter">
Age Counter
</Link>
</li>
</ul>
</div>
</nav>
);
}
}
export default withRouter(Navbar);
Demo working Code Sandbox
Check this one
class LiNavLink extends React.Component<NavLinkProps> {
render() {
return (
<Route exact={this.props.exact} path={this.props.to.toString()}>
{
({ match }) =>
<li className={match ? 'active' : undefined}>
<Link to={this.props.to} >
{this.props.children}
</Link>
</li>
}
</Route>
);
}
}
I am just starting with the react, so not sure if this is the best practices, but after going through router v4 docs, I used withRouter props -> location.pathname and compared it to my route.
Here is the Navigation.js:
import React from 'react';
import { withRouter } from 'react-router-dom';
import NavLink from '../General/NavLink';
const activeClass = (path, link) => {
if (path === link) {
return true;
}
return false;
};
const Navigation = props => {
const { location } = props;
return (
<ul className="menu menu--main nano-content">
<NavLink
to="/"
parentClass={
activeClass(location.pathname, '/')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Dashboard
</NavLink>
<NavLink
to="/users"
parentClass={
activeClass(location.pathname, '/users')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Users
</NavLink>
<NavLink
to="/projects"
parentClass={
activeClass(location.pathname, '/projects')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Projects
</NavLink>
<NavLink
href="http://google.com"
parentClass="menu__item"
linkClass="menu__link effect effect--waves"
>
Google
</NavLink>
</ul>
);
};
export default withRouter(Navigation);
From there you have parent and child classes that you can use on child component.
I found that by using CSS you can make the active link expand to fill up it's parent <li> element by setting display:block; in the active class.
For example if our link was:
<li>
<NavLink to="/overview" className=styles.sideLink activeClassName=styles.sideLinkSelected>
Overview
</NavLink>
</li>
then our CSS would be:
&__sideLinkSelected
{
background-color: blue;
display:block;
}

Resources