I want to disable Link in some condition:
render() {
return (<li>{this.props.canClick ?
<Link to="/">Test</Link> :
<a>Test</a>}
</li>)
}
<Link> must specify to, so I can not disable <Link> and I have to use <a>
You could just set set the link's onClick property:
render () {
return(
<li>
{
this.props.notClickable
? <Link to="/" className="disabledCursor" onClick={ (event) => event.preventDefault() }>Link</Link>
: <Link to="/" className="notDisabled">Link</Link>
}
</li>
);
};
Then disable the hover effect via css using the cursor property.
.disabledCursor {
cursor: default;
}
I think that should do the trick?
Your code already seems quite clean and slim. Not sure why you want an "easier" way. I'd do it exactly how you're doing it.
However, here are a few alternatives:
Using pointer-events
This one is probably as short and sweet as you can get it:
render() {
return (<li>
<Link to="/" style={this.props.canClick ? {pointerEvents: "none"} : null}>Test</Link>
</li>)
}
Using onClick listener
As an alternative, you could use a generic <a> tag and add an onClick listener for the condition. This is probably better suited if you have lots of links that you want to control their state because you could use the same function on all of them.
_handleClick = () => {
if(this.props.canClick) {
this.context.router.push("/");
}
}
render() {
return (
<li>
<a onClick={this._handleClick}>Test</a>});
</li>
);
}
The above will work assuming you are using context.router. If not, add to your class:
static contextTypes = {
router: React.PropTypes.object
}
Better version of OP code
As I mentioned above, I still think your approach is the "best". You could replace the anchor tag with a span, to get rid of the styling for a disabled link (e.g pointer cursor, hover effects, etc).
render() {
return (<li>{this.props.canClick ?
<Link to="/">Test</Link> :
<span>Test</span>}
</li>)
}
A good solution is using onClick() with event object. just do this in your jsx:
<Link to='Everything' onClick={(e) => this._onClick(e)}
and in your _onClick function:
_onClick = (e) => {
e.preventDefault()
}
Complete Example in React:
import React, { Component } from 'react'
import {Link} from 'react-router-dom'
export default class List extends Component {
_onClick = (e) => {
e.preventDefault()
}
render(){
return(
<div className='link-container'>
<Link to='Something' onClick={e => this._onClick(e)}
</div>
)
}
}
Just making the URL null seems to do the trick:
const url = isDisabled ? null : 'http://www.stackoverflow.com';
return (
<a href={url}>Click Me</a>
);
In short easier way to disable link in React?
<Link to="#">Text</Link>
I think you should you atrribute to=null to set disable a link.
<Link to=null />
Your code:
render() {
return (<li>
<Link to={this.props.canClick?'/goto-a-link':null} >Test</Link>
</li>)
}
Passing # in to prop to the Link should do the trick for you
You can define link as per your requirement. if you want to disable it just pass # in props.link
render() {
return (<li><Link to={props.link}>Test</Link></li>);
}
I didn't like any of the answers. Surprisingly, if you are using bootstrap, assigning the class disabled will make the link inactive. So, no need to change the path to # or anything.
<Link to='something/else' className='nav-link disabled'>Transactions Detail</Link>
Use this in your link tag. I'm using it in functional component and it's working fine.
<Link style={{pointerEvents: 'none'}}>
You can conditionally set the to prop of the Link component. When you set it to #, the link would be disabled.
render() {
return (
<li>
{
<Link to={`${this.props.canClick ? '/' : '#'}`}>Test</Link>
}
</li>
)
}
Related
I have a button styled as a Font Awesome.
<button
onClick={this.changeLockButtonStyle}
id="LockButton"
>
<FaLockOpen />
</button>
I'm trying to find a way to change the style to <FaLockClosed /> From reading up online the only examples I see are regarding JQuery and class=”fas fa-lockclosed” toggle class. But I am using a button, not the normal icon. I have tried document.getElementById('LockButton').innerHTML= '<FaLockClosed />' but doesnt work. I'd like to avoid using JQuery if possible.
Here you go:
const [isLocked, setIsLocked] = useState(false);
return (
<button
type="button"
onClick={() => { setIsLocked(true); }}
>
{isLocked ? <FaLockClose /> : <FaLockOpen />}
</button>
);
Update:
Thats how you do it in class component.
constructor(props) {
super(props);
this.state = {
isLocked: false
};
this.lockIcon = this.lockIcon.bind(this);
}
lockIcon() {
this.setState({ isLocked: true });
}
render() {
const { isLocked } = this.state;
return (
<button
type="button"
onClick={this.lockIcon}
>
{isLocked ? <FaLockClose /> : <FaLockOpen />}
</button>
);
}
My best practice solution is using css class.
But if you can't do it , you can use display state for the 2 icons that controlled by a javascript variable.
If you using react or angular, I would just toggle the component depending on a variable set during button pushed.
Reference on how to do the toggle in react
https://stackoverflow.com/a/46425155/14167216
Here is a jQuery example.
You can set the class on the button and then check if button has class. If it has lock class then change to unlock class, and vice-versa. Check the sample code below.
function changeLockButtonStyle() {
var icon = $('#LockButton')
var hasLock = icon.hasClass('fa-lock');
if(hasLock) {
icon.removeClass('fa-lock').addClass('fa-unlock');
} else {
icon.removeClass('fa-unlock').addClass('fa-lock');
}
}
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<button
onClick='changeLockButtonStyle()'
id="LockButton"
class="fa fa-lock"
>
</button>
</body>
I'm trying to rewrite one of my JS plugins to react, as a way of learning.
I have a panel that when hidden/shown needs to be updated with several classnames as well as some that need to wait for a css animation to complete (why the timer).
How should I do this in a react way? Using querySelector to change classnames seem very wrong..?
Detailed explanation
When showPanel is triggered the following need to happen
the body/html element need updated css (hence me adding classes)
an existing overlay fades in (adding a class to that)
the modal div is displayed (adding a class for that)
the modal div is told to be active AFTER the animation has been run (hence the timer and class "am-animation-done")
What I preferably would like to have/learn is best practice to do this in reactjs. I'm thinking a toggle state that when triggered sets the state to visible/hidden and if set to "visible" the class changes below happens. My biggest issue is the timer thing.
showPanel = () => {
document.querySelector('body').classList.add('am-modal-locked');
document.querySelector('html').classList.add('am-modal-locked');
document.querySelector('.am-overlay').classList.add('fadein');
document.querySelector('.am-modal').classList.add('am-show');
const timer = setTimeout(() => {
document.querySelector('.am-modal').classList.add('am-animation-done');
}, 500);
return () => clearTimeout(timer);
};
hidePanel = () => {
document.querySelector('.am-modal').classList.remove('am-show');
document.querySelector('.am-modal').classList.remove('am-animation-done');
document.querySelector('.am-overlay').classList.add('fadeout');
const timer = setTimeout(() => {
document.querySelector('.am-overlay').classList.remove('fadein');
document.querySelector('.am-overlay').classList.remove('fadeout');
document.querySelector('body').classList.remove('am-modal-locked');
document.querySelector('html').classList.remove('am-modal-locked');
}, 500);
return () => clearTimeout(timer);
};
Source code updated for clarifaction
This is a lot simpler in React, here's an example with hooks
function Panel() {
const [hidden, setHidden] = useState(false);
const toggleCallback = useCallback(() => setHidden(hidden => !hidden), []);
const cls = hidden ? 'hide' : 'show';
return (
<div className={cls}>
<button onClick={toggleCallback}>Toggle</>
</div>
)
}
You can use the state to dinamically change classnames inside your component
className={this.state.isPanelVisible}
And maybe instead of setting it as boolean you can set your variable to the class you need at the moment.
React working with virtual DOM so you should play with state and change class of that particular element like below example:
constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}
handleClick(id) {
if(this.state.active){
this.setState({'active': false,'class': 'album'})
}else{
this.setState({'active': true,'class': 'active'})
}
}
<div className={this.state.class} onClick={this.handleClick.bind(this.data.id}>
<p>Data</p>
</div>
In very basic use cases you can write the logic inside of the class itself.
<div className={active ? "active" : "disabled"} />
In more advanced cases I would suggest to use something like classnames package.
https://www.npmjs.com/package/classnames
<div className={classNames({ foo: true, bar: true, boo: false })} />
Which would result in div having class foo and bar
This is mainly regarding one component, but if you really have to affect class of something so far away as body would be, than you are most likely gonna need useQuerySelector or put the state somewhere high and then base the logic on it.
Yes that's not a very good way to do it. Instead you should use state variables to toggle your classes as well. There is no need to manually manipulate DOM. The you can set up your timeout inside the callback of your first setState to change state again.
Maybe something like this:
class Todo extends Component {
constructor(props) {
super(props);
this.state = {
class1: 'on',
class2: 'off'
}
}
toggle = () => {
this.setState({class1: 'off'}, () => {
setTimeout(() => {
this.setState({class2: 'on'})
}, 2000)
})
}
render() {
const {class1, class2} = this.state;
return (
<div>
<h1 className={`${class1} ${class2}`} onClick={this.toggle}>Class toggle</h1>
</div>
)
}
}
use this approach for change styling on state change
<div className={`rest_of_classes ${isClassChange ? 'change_class_name': ''}`} />
I want my side bar button to change color when I route to its linked page.
my code changes the color and the color remains the same even if I route to a different page.
here is my code:
lass Sidebar extends Component<{}, {}> {
style = this.getBtnClassName();
render() {
return (
<aside className="sidebar">
<nav className="nav">
<LinkButton to="/timer" label="Timer" cssPrefix={this.getBtnClassName()} styles={['block']} />
<LinkButton to="/reports" label="Report" cssPrefix={this.getBtnClassName()} styles={['block']} />
</nav>
</aside>
);
}
getBtnClassName() {
if (window.location.href === 'http://localhost:3000/timer') {
return 'nav_active';
} else {
return 'nav__btn';
}
}
}
export default Sidebar;
You can use className instead.
Start out by simply making your getBtnClassname simply use the window.location.pathname and not the window.location.href, then it can also work when deployed to any website.
getBtnClassName() {
if (window.location.pathname === '/timer') {
return 'nav_active';
} else {
return 'nav__btn';
}
}
From there, all you really need to do is call the function to assign the returned value of this.getBtnClassName to be a classname you wish to apply.
<button to="/timer" label="Timer" className={this.getBtnClassName()}>{this.getBtnClassName()}</button>
You could also assign the correct class to a variable inside the render, which when you only have 2 classes in your case is a little cleaner.
const btnClass = window.location.pathname === "/timer" ? "nav_active" : "nav__btn"
<button to="/reports" label="Report" className={btnClass}>{btnClass}</button>
Example:
https://stackblitz.com/edit/react-scltse
you can use NavLink from react-router-dom
and use the prop activeClassName
ref: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/NavLink.md
I am new to react and am trying to toggle a body class using two different buttons. One is supposed to add a class using an onClick event and the other is supposed to remove the class. Below is an example of my code.
Right now in the console I can see the event fire twice but the class remains. As I stated I am new to React so I know I may be doing this incorrectly.
bodyFixed() {
document.body.classList.add('body-fixed');
}
bodyRelative() {
document.body.classList.remove('body-fixed');
}
You are trying to modify the dom directly like you would with vanilla js or JQuery, but this is not how react is meant to be used. React creates a virtual dom that you create and manage, and then react handle changing the page for you.
I recommend following a guide like this one to learn basic setup and concepts (skip to the part where he uses JSX).
I can further point you in the right direction if you show your whole component file.
You want to toggle a className prop value in the React way.
The React way is having a state prop and having a handler function that will toggle the state value, rather than manipulating the DOM node directly (the way you're doing it).
I would suggest you to take a look at React Main Concepts: Handling events and later once you feel a little bit more comfortable to read about Virtual DOM and Reconciliation in React.
Here's how can you do it:
const { classNames } = window
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
isToggled: true
}
this.toggleClass = this.toggleClass.bind(this)
}
toggleClass() {
const { isToggled } = this.state
this.setState({
isToggled: !isToggled
})
}
render() {
const { isToggled } = this.state
const className = classNames({
'body-fixed': isToggled
})
return <div className={className}>
<div>Current `className`: <b>{ className }</b></div>
<button onClick={this.toggleClass}>Toggle class</button>
</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://unpkg.com/classnames#2.2.6/index.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>
I was able to use the code I listed earlier. I had my onClick events positioned incorrectly. Here is an example of the code I used:
bodyFixed() {
document.body.classList.add('body-fixed');
}
bodyRelative() {
document.body.classList.remove('body-fixed');
}
<Toggle>
{({on, getTogglerProps, setOn, setOff, toggle, showAlert}) =>
<div>
<button className="search-icon-top" {...getTogglerProps()}>{on ? <img className="times" onClick={this.bodyRelative} src={require('img/times.svg')} alt=" " /> : <i className="fa fa-search" onClick={this.bodyFixed}></i>}</button>
<div>
{on ? <TodaySearchBox /> : ''}
</div>
</div>}
</Toggle>
This is just a start for now. Thank you for the input.
EDIT: I am open to suggestions. Like I said I am new to React.
I am having an issue where the "right click" on the links that are created with React Router Link tags.
I want to be able to right click on those links and select "Open Link in New Tab" option.
Could anyone please help me with this issue?
Here is my code:
redirectUrl = (e) => {
let url = e.currentTarget.getAttribute("dataattrurl");
browserHistory.push({
pathname : url,
query : '',
state : {}
});
}
const listItems = readingHistory.map((item, index) => {
return (
<li key={index} role="listitem">
<Link className="linkPointer" onClick={this.redirectUrl} dataattrurl={`/document/${item.resId}`} onContextMenu={this.contextMenu}>
{item.title}
</Link>
</li>
);
});
The problem is that you are handling the click event with an onClick rather than with an href (to for Link), so the browser does not register this as a link to another page but rather an element with a click event listener.
What you should do is make use of the to attribute of Link, which is basically the href for a regular a tag. See the docs here: https://reacttraining.com/react-router/web/api/Link
This will also result in much cleaner code. All your code can be reduced to:
const listItems = readingHistory.map((item, index) => {
return (
<li key={ index } role="listitem">
<Link className="linkPointer" to={ `/document/${item.resId}` } onContextMenu={ this.contextMenu }>
{ item.title }
</Link>
</li>
);
});
So the redirectUrl can be removed entirely.