ReactJS + React Router: How to disable link from React-Router's <Link>? - javascript

Using React-Router's Link, I have a set up like so:
<Link to={...}>{this.props.live? "Live": "Not Live"}</Link>
Where if this.props.live gets passed in, I would like to simply display a text "Live" which will direct to a new page. If this.props.live is not passed in, want to display the text "Not Live" but remove the hyperlink from it/deactivate/disable it.
Is it possible to do so? I tried <div style={{pointerEvents: "none"}}>Not Live</div>, but does not work.
Will be upvoting and accepting answer. Thank you!

You should do this:
let component;
component=this.props.live? <Link to={...}> Live </Link>: <div> Not Live </div>
render() {
{component}
}
Cheers:)

this.props.live
? <Link to={...}> Live </Link>
: <div> Not Live </div>

You can prevent the page to be redirected using evt.preventDefault()
class MyComponent extends React.Component {
constructor(props) {
this.handleLinkClick = this.handleLinkClick.bind(this);
}
handleLinkClick(evt) {
if (!this.props.live) {
evt.preventDefault(); // will prevent redirection
}
}
render() {
<Link onClick={this.handleLinkClick} to={...}>
{this.props.live? "Live": "Not Live"}
</Link>
}
}
Shorthand but controversial version
<Link onClick={evt => !this.props.live && evt.preventDefault()} to={...}>
{this.props.live? "Live": "Not Live"}
</Link>
See the API docs

Related

Display a single element of an array according to its ID REACT

I would like to make an articles system on my site created with React, but I'm a bit lost.
I have a JS array that allows me to make a loop to display my articles. When I click on an article, it sends me to the right page thanks to the <Link />. But here is my problem, how to display the content of the article I clicked on? I tried to get the slug of the article and to compare it to the ID of the elements of my array but I have to admit that I don't know where I stand.
Here is my code, it only returns a <h1>Title: { id }</h1> (which is the correct slug every time)
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams
} from "react-router-dom";
import '../styles/Single.css'
const posts = [
{
source:
"./images/esdexp-th-homepage.jpg",
content: {
name: "Expérience ESD",
link:"/post/ESDexp",
idSlug :"ESDexp"
}
},
{
source:
"./images/vacui-th-homepage.jpg",
content: {
name: "Horror Vacui",
link:"/post/horrorvacui",
idSlug : "horrorvacui"
}
},
{
source:
"./images/meteo-th-homepage.jpg",
name: "Forecast",
link:"/post/forecast",
idSlug :"forecast"
},
{
source:
"./images/ami-th-homepage.jpg",
name: "Citroën Ami",
link:"/post/ami",
idSlug :"ami"
},
{
source:
"./images/musee-th-homepage.jpg",
content: {
name: "Musée Home",
link:"/post/musee",
idSlug :"musee"
}
},
];
export default function ParamsExample() {
return (
<Router>
<Switch>
<Route path="/post/:id" children={<Child />} />
</Switch>
</Router>
);
}
function Child() {
let { id } = useParams();
console.log({id})
var itemPost = posts.filter(function(displayPost) {
return displayPost.idSlug == { id };
});
console.log(itemPost)
return (
<div>
<div className="single-container rev-block">
<div className="single-content-container">
<h1>Title : {id}</h1>
</div>
</div>
</div>
);
}
I hope I've made this as clear as possible,
Thanks in advance!
It is not working because the posts data is not consistent. The idSlug of some posts are inside the content object.
To handle that situation you have to check if the idSlug is not undefined, and if it is, set the post slug or ID to use the idSlug inside the content object.
A working example can be found here.
Sandbox: https://codesandbox.io/s/angry-cannon-7kxiz?file=/src/App.js
The useParams has been replaced with an actual ID or slug of a post because the sandbox does not allow routing.
This line handles the inconsistency of the posts data.
let post_slug = displayPost.idSlug
? displayPost.idSlug
: displayPost.content.idSlug;
Also, please note that filter returns another array, so you have to access post or the itemPost using the first index.
<h1>Title : {itemPost[0].content.name}</h1>
And of course, you can also use the id returned by the useParams.
try this.
function Child() {
let { id } = useParams();
console.log({id})
var itemPost = posts.filter(function(displayPost) {
return displayPost.idSlug === id ; // you don't need those curly braces.
});
console.log(itemPost)
return (
<div>
<div className="single-container rev-block">
<div className="single-content-container">
<h1>Title : {id}</h1>
</div>
</div>
</div>
);
}
Get the help of <Link /> to pass the state from parent component to child component. Make the parent component like this:
<Link to={{
pathname:{'/post/post.id'},
state:{
post:post
}
}}>
Then you can get the post in the child component as below:
function child(props){
const post = props.location.state.post
}
Also, try to make the routing as below. It won't make much complications:
<Route path="/post/:id" component={Child} />

Trying to display text, but no luck. However, I check my console and see empty divs with p tags. App is not breaking

I am in the process of working on a joke app, which I am building from scratch by myself with no tutorial. Right now, there is a component SportsJokesApi, that pulls in the data from a local json folder (SportsJokesData) I created. This is how it is set up:
const SportsJokesData = [
{
id: "1",
question: "What did the baseball glove say to the ball?",
answer: "Catch ya later!"
}
]
export default Sports Jokes Data
On the landing page, when the user selects the Sports Jokes category, they will be taken to a new page which will display jokes related to sports. Right now, you need to click the button (Click here for a joke) in order for the first joke to display. I would love for the first random joke to already be displaying when the user selects this category and gets taken to that page for the first time.
This is how I have the component set up. In my initial approach I created a separate function called getInitialJoke, which returns a random joke. Then in the render, I created a variable called const {initialJoke} which is this.getInitialJoke(); and then the joke would be displayed. Since state update is asynchronous, I did some safety checking by using initialjoke?.answer. When I go back and run the app, no joke appears and the text does not show up. However, I do see new divs with empty p tags on console. Does anyone know what could be wrong and how I can fix this? Again, I want a joke to already be displaying. The way it is set up in this component, you have to click on the Click Here for a Joke button in order to see the first joke.
import React from 'react'
import SportsJokesData from '../data/SportsJokesData';
import { Link } from 'react-router-dom';
import './Buttons.css';
const initialState = {
randomJoke: {}
};
class SportsJokesApi extends React.Component {
constructor(props) {
super(props);
this.getRandomJoke = this.getRandomJoke.bind(this);
this.state = initialState;
}
getInitialJoke() {
return SportsJokesData[(SportsJokesData.length * Math.random()) << 0];
}
getRandomJoke() {
this.setState({
randomJoke: SportsJokesData[(SportsJokesData.length * Math.random()) << 0]
});
}
render() {
const {randomJoke} = this.state;
const {initialJoke} = this.getInitialJoke();
return (
<React.Fragment >
<div>
<p>{initialJoke?.question}</p>
</div>
<div>
<p>{initialJoke?.answer}</p>
</div>
<div className="flex">
<p>{randomJoke.question}</p>
</div>
<div className="flex">
<p>{randomJoke.answer}</p>
</div>
<div className="flex">
<button class="btn joke" onClick = {this.getRandomJoke}>Click here for joke </button>
</div>
<div className="flex">
<Link to="/ProgrammingJokes">
<button className="btn programming">Programming Jokes</button>
</Link>
</div>
<div className="flex">
<Link to="/DadJokes">
<button className="btn dad">Dad Jokes</button>
</Link>
</div>
<div className="flex">
<Link to="/SpanishJokes">
<button className="btn spanish">Chistes en ñ</button>
</Link>
</div>
<div className="flex">
<Link to="/">
<button className="btn home">Home Page</button>
</Link>
</div>
</React.Fragment >
);
}
}
export default SportsJokesApi;
Perhaps you could call the initialJoke() function in one of the React lifeCycle Events, for example with something like this:
// Add this to your code fellow.
componentDidMount() {
this.getInitialJoke();
}
For more information on the life cycle events and how they work, have a look at this great article, buddy.
React: Component Lifecycle Events

Firebase switch header option with onAuthStateChanged

I'm sorry if my question has already asked.
I am a beginner on react and I really want to learn how to do this and understand.
I use Firebase on my React.JS project and i want switch a part of header when the user has connected or not.
I think using the conditional-rendering but on the firebase function after the (if) does not allow me to do setState().. or i have an error
So I would be very happy if we could help me or give me a track of where to look for the answers !
class Header extends Component {
state={
connected: null
}
render(){
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
} else {
}
});
return (
<div className="backgroundheader">
<div className="liensheader">
<a href="/" className="lili" style={{textDecoration: "none"}}>Home</a>
<a href="/event" className="lili" style={{textDecoration: "none"}}>Manifestations</a>
<a href="/commerces" className="lili" style={{textDecoration: "none"}}>Commerces</a>
<a href="/tips" className="lili" style={{textDecoration: "none"}}>Tips</a>
</div>
{/* options header when the user is disconnect */}
{/* <Deader /> */}
{/* options header when the user is connected */}
{/* <Ceader /> */}
</div>
);
}
};
export default Header;
Thank's you and sorry for my bad english :/
The onAuthStateChanged fires asynchronously, so you can't directly use it inside your render method. Whenever you have a situation like this, the solution is to put the asynchronous value into the state, and then use the state in the render method.
So something like:
class Header extends Component {
state={
connected: null
}
componentDidMount() {
firebase.auth().onAuthStateChanged(function(user) {
setState({ user: user });
});
}
render(){
return (
<div className="backgroundheader">
<div className="liensheader">
<a href="/" className="lili" style={{textDecoration: "none"}}>Home</a>
<a href="/event" className="lili" style={{textDecoration: "none"}}>Manifestations</a>
<a href="/commerces" className="lili" style={{textDecoration: "none"}}>Commerces</a>
<a href="/tips" className="lili" style={{textDecoration: "none"}}>Tips</a>
</div>
{ user ? <Deader /> : <Ceader /> }
</div>
);
}
};
export default Header;
Also see:
Firebase storage: download images to and put to img src in React map. The same problem of asynchronously loaded data, but then with getting the download URL of images in Cloud Storage.
How to update Firebase data to the React application in realtime. This highlights the same problem of asynchronously loaded data, but now with getting JSON data from the Realtime Database.

ReactJS - how to get current page in the URL?

I have a menu in my React app, here's the Menu component:
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
active: '/'
}
}
render() {
return (
<nav>
<ul className='list-inline'>
<li>
<Link to='/' style={this.state.active === '/' ? activeStyle : {}} onClick={this.handleClick.bind(this, '/')}>
Home
</Link>
</li>
<li>
<Link to='/contact' style={this.state.active === 'contact' ? activeStyle : {}} onClick={this.handleClick.bind(this, 'contact')}>
Contact
</Link>
</li>
)
}
}
export default Menu
When I click on a menu item, the clicked item will get higlighted (bold). But if I have in the URL some page (eg. http://localhost:3001/contact) and refresh the page, this Contact item in the menu will not be highlighted (because highlighting is made based on the onClick event).
I tried to parse the content of URL with import * as qs from 'query-string'; // for fetching the URL paramaters and then I tried to display the URL content:
console.log(qs.parse(location.search));
But the output is only {} instead of the desired contact (so I could highlight this item in the menu).
Thus, when a URL is loaded straight (without click on the respective menu item), how do I catch this situation and highlight the respective menu item?
Thank you
React router has a component for exactly this situation
https://reacttraining.com/react-router/web/api/NavLink
You can change the component from Link to NavLink and add the component will check the url in which you are.
<Link to='/contact' style={this.state.active === 'contact' ? activeStyle : {}} onClick={this.handleClick.bind(this, 'contact')}>
Contact
</Link>
<NavLink
to="/contact"
activeStyle={activeStyle}
>Contact</NavLink>
Also, you can check the URL params from the router instance:
https://reacttraining.com/react-router/web/example/url-params

React.js implement menu [highlight active link]

The following React.js code renders a navbar with two links named 'about' and 'project'. On page load the 'about' link is active and colored red. When the other link is clicked the state of the navbar is set to 'project', 'about' link style is set back, and 'project' is colored red.
I achieve this by attaching a click handler to both link tags, and set the state of active to the name of the event.target.innerHTML.
I'm new to react, and I feel this is a really verbose way of going about this. I am aware that there is an activeClassName prop you can pass to a react-router link, but I want a way that does not use it.
import React, { Component } from 'react'
import { Link, Route } from 'react-router'
export default class Navbar extends Component {
constructor() {
super();
this.state = {
active: 'about'
}
this._handleClick = this._handleClick.bind(this);
}
_handleClick(e) {
this.setState({
active: e.target.innerHTML
});
}
render() {
let aboutStyle;
let projectStyle;
if (this.state.active === 'about') {
aboutStyle = { color: '#ff3333' };
projectStyle = {};
} else {
aboutStyle = {};
projectStyle = { color: '#ff3333' };
}
return (
<div className='navbar'>
<Link to='/'><h2>BK //</h2></Link>
<div className='menu'>
<Link style={aboutStyle} onClick={this._handleClick} to='about'>about</Link>
<Link style={projectStyle} onClick={this._handleClick} to='projects'>projects</Link>
</div>
</div>
)
}
}
At this day you can use NavLink from react-router-dom. This object supports attributes as activeClassName, activeStyle, or isActive (for functions).
import { NavLink } from 'react-router-dom';
<NavLink to='about' activeClassName="active">about</NavLink>
// Or specifing active style
<NavLink to='about' activeStyle={{color: "red"}}>about</NavLink>
// If you use deep routes and you need an exact match
<NavLink exact to='about/subpath' activeClassName="active">about</NavLink>
For more options read documentation: https://reacttraining.com/react-router/web/api/NavLink
maybe slightly less verbose... in Pseudocode
const menuItems = [
'projects',
'about',
];
class MenuExample extends React.Component {
_handleClick(menuItem) {
this.setState({ active: menuItem });
}
render () {
const activeStyle = { color: '#ff3333' };
return (
<div className='menu'>
{menuItems.map(menuItem =>
<Link
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
>
{menuItem}
</Link>
)}
</div>
);
}
}
Using NavLink in place of Link gives you the opportunity to set your preferred style for the active page/link.
So you can go ahead and set the style in CSS like this.
.active {
color: 'red'
}
We can use NavLink to highlight the active by writing custom class handling based on de-structured isActive property
<NavLink
to='/'
className={
useCallback(({isActive}) => isActive ? classes.active : classes.link)}
>
Home
</NavLink>
Ref: https://stackoverflow.com/a/72520423/4652706
As of React Router v6, activeClassName prop will not work. Find the solution here, ReactJS - Unknown prop `activeClassName` on <a> tag. Remove this prop from the element

Categories