How to disable a link in reactjs? - reactjs

I have this in my reactjs app:
import Link from 'react-router/lib/Link'
Been trying to disable this link but this does not have the desired effect:
<Link disable={true}/>
It just renders it invisible. How can I disable( based on a condition) the reactjs Link?

Contain many issues on react-router, there is no support disabled attribute in Link Component, so you can try some with this issue:
1. onClick event
Use preventDefault() to handle onClick event.
/* YourComponent.js */
class YourComponent extends React.Component {
render() {
return (
<Link onClick={e => e.preventDefault()} />
);
}
}
2. CSS's pointer-events attribute
/* YourComponent.js */
class YourComponent extends React.Component {
render() {
return (
<Link className='disabled-link' />
);
}
}
/* css file */
.disable-link {
pointer-events: none;
}
or you can use inline style
/* YourComponent.js */
class YourComponent extends React.Component {
render() {
return (
<Link style={{ pointerEvents: 'none' }} />
);
}
}
What I used was method 2, it's more clearly for me on my project.

Another option is to have a function return 2 different links based on some condition....
const fnSomePath = () =>{
return somecondition ? `www.abc.xyz` : `#`
}
Then call the function where your link is being used:
<ListGroupItem>
<NavLink to={{pathname: fnSomePath()}}>
TEXT
</NavLInk>
</ListGroupItem>

You could conditionally render something that looks like a disabled link based upon some state.
For instance in typescript:
export interface Location {
pathname: string;
search: string;
state: any;
hash: string;
key ?: string;
}
interface LinkProps {
to: string | Location
replace?:boolean
}
interface DisableLinkProps extends LinkProps {
enabled: boolean
linkText:string
}
export class DisableLink extends React.Component<DisableLinkProps, undefined> {
render() {
var element= this.props.enabled ? <span className="disableLinkDisabled">{this.props.linkText}</span> : <Link to={this.props.to} replace={this.props.replace}>{this.props.linkText}</Link>
return element;
}
}
interface DemoClassState {
linkEnabled:boolean
}
export class DemoClass extends React.Component<undefined, DemoClassState> {
constructor(props) {
super(props);
this.state = { linkEnabled:false }
}
toggleLinkEnabled = () => {
this.setState((prevState) => {
return {
linkEnabled: !prevState.linkEnabled
}
});
}
render() {
return <div>
<DisableLink enabled={this.state.linkEnabled} to="/somewhere" linkText="Some link" />
<button onClick={this.toggleLinkEnabled}>Toggle link enabled</button>
</div>
}
}

This is actually a bit tricky. And maybe even somewhat ill-advised.
https://css-tricks.com/how-to-disable-links/
The route (no pun intended) I took was to not render the link.
I composed a new component from react-router's Link.
import React from 'react';
import { Link } from 'react-router-dom';
export default function ToggleableLink(props) {
const { disabled, ...rest } = props;
return disabled ? props.children : <Link {...rest}>{props.children}</Link>;
}
Usage:
<ToggleableLink disabled={!showTheLink}>Foobar</ToggleableLink>

Related

Not sure if i'm using react context correcly

I've created a form in react and after some research i think that if you don't want to use an external library to manage the form, the context could be the best choice, expecially in my case where i've many nested component that compose it.
But, i'm not sure that putting a function inside my state is a good thing.
But let me give you some code:
configuration-context.js
import React from 'react'
export const ConfigurationContext = React.createContext();
ConfigurationPanel.jsx
import React, { Component } from 'react'
import { Header, Menu, Grid } from 'semantic-ui-react'
import ConfigurationSection from './ConfigurationSection.jsx'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationPanel extends Component {
constructor(props) {
super(props)
this.state = {
activeItem: '',
configuration: {
/* the configuration values */
banana: (data) => /* set the configuration values with the passed data */
}
}
}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
render() {
return (
<ConfigurationContext.Provider value={this.state.configuration}>
<Grid.Row centered style={{marginTop:'10vh'}}>
<Grid.Column width={15} >
<div className='configuration-panel'>
/* SOME BUGGED CODE */
<div className='configuration-section-group'>
{this.props.data.map((section, i) => <ConfigurationSection key={i} {...section} />)}
</div>
</div>
</Grid.Column>
</Grid.Row>
</ConfigurationContext.Provider>
)
}
}
ConfigurationItem.jsx
import React, { Component } from 'react'
import { Input, Dropdown, Radio } from 'semantic-ui-react'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationItem extends Component {
static contextType = ConfigurationContext
constructor(props) {
super(props)
}
handleChange = (e, data) => this.context.banana(data)
itemFromType = (item) =>{
switch (item.type) {
case "toggle":
return <div className='device-configuration-toggle-container'>
<label>{item.label}</label>
<Radio name={item.name} toggle className='device-configuration-toggle'onChange={this.handleChange} />
</div>
/* MORE BUGGED CODE BUT NOT INTERESTING*/
}
}
render() {
return this.itemFromType(this.props.item)
}
}
So, at the end i've a ConfigurationContext that is just a declaration, everything is inside the parent state.
The thing that i don't like is putting the banana function inside the state (it will have more logic that just logging it)
What do you think about it?
Any suggestion is appreciated.
Thanks
banana is just a regular function and you do not have to put it in the state, just do:
class ConfigurationPanel extends Component {
banana = data => console.log(data)
...
render() {
return (
<ConfigurationContext.Provider value={{banana}}>
...
}
After that you can use this.context.banana(data) as normal.

Confused by how to pass correctly props to descendant components

I have a question. Example I have 4 components and A includes B, B includes C and C includes D.
I have a state { mode: 'dashboard' } and function changeMode(mode) => { this.setState({mode: mode})} in A. Without Context or Redux,
i want to use the function in D.
1.So should i pass the function like:
<B changeMode={this.changeMode} />
and in B will be <C changeMode={this.props.changeMode} />
and so on.
2.Or will like: <B changeMode={this.changeMode} />
and in B we will declare a function: changeMode = (mode) => {this.props.changeMode(mode)} and so on.
What is the better way and why? Thanks in advance.
If you only want to pass the function down to your child component you can do it this way
import React, { Component } from "react";
class App extends Component {
state = {
mode: "dashboard"
};
changeMode = () => {
this.setState({ mode: "mode" });
};
render() {
console.log(this.state.mode);
return <Second mode={this.changeMode} />;
}
}
class Second extends Component {
render() {
return <Third mode={this.props.mode} />;
}
}
class Third extends Component {
render() {
return <Fourth mode={this.props.mode} />;
}
}
class Fourth extends Component {
render() {
return <button onClick={this.props.mode}>mode</button>;
}
}
export default App;
You could use the second option if you would need to pass argument to parent component

React Higher Order Component that detects dom events that takes functional components as arg

I have a scenario where I want to create an HOC that detects mouse events (e.g. mouseenter, mouseleave) when they occur on the HOC's WrappedComponent, then pass the WrappedComponent a special prop (e.g. componentIsHovered). I got this working by using a ref callback to get the wrapped component instance, then adding event listeners to the wrapped instance in my HOC.
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
export default (WrappedComponent) => {
return class DetectHover extends Component {
constructor(props) {
super(props)
this.handleMouseEnter = this.handleMouseEnter.bind(this)
this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.bindListeners = this.bindListeners.bind(this)
this.state = {componentIsHovered: false}
this.wrappedComponent = null
}
componentWillUnmount() {
if (this.wrappedComponent) {
this.wrappedComponent.removeEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.removeEventListener('mouseleave', this.handleMouseLeave)
}
}
handleMouseEnter() {
this.setState({componentIsHovered: true})
}
handleMouseLeave() {
this.setState({componentIsHovered: false})
}
bindListeners(wrappedComponentInstance) {
console.log('wrappedComponentInstance', wrappedComponentInstance)
if (!wrappedComponentInstance) {
return
}
this.wrappedComponent = ReactDOM.findDOMNode(wrappedComponentInstance)
this.wrappedComponent.addEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.addEventListener('mouseleave', this.handleMouseLeave)
}
render() {
const props = Object.assign({}, this.props, {ref: this.bindListeners})
return (
<WrappedComponent
componentIsHovered={this.state.componentIsHovered}
{...props}
/>
)
}
}
}
The problem is that this only seems to work when WrappedComponent is a class component — with functional components the ref is always null. I would just as soon place the WrappedComponent inside <div></div> tags in my HOC and carry out the event detection on that div wrapper, but the problem is that even plain div tags will style the WrappedComponent as a block element, which doesn’t work in my use case where the HOC should work on inline elements, too. Any suggestions are appreciated!
You can pass the css selector and the specific styles you need to the Higher Order Component like this:
import React, {Component} from 'react';
const Hoverable = (WrappedComponent, wrapperClass = '', hoveredStyle=
{}, unhoveredStyle={}) => {
class HoverableComponent extends Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
}
}
onMouseEnter = () => {
this.setState({hovered: true});
};
onMouseLeave = () => {
this.setState({hovered: false});
};
render() {
return(
<div
className={wrapperClass}
onMouseEnter= { this.onMouseEnter }
onMouseLeave= { this.onMouseLeave }
>
<WrappedComponent
{...this.props}
hovered={this.state.hovered}
/>
</div>
);
}
}
return HoverableComponent;
};
export default Hoverable;
And use Fragment instead of div to wrap your component:
class SomeComponent extends React.Component {
render() {
return(
<Fragment>
<h1>My content</h1>
</Fragment>
)
}
And then wrap it like this
const HoverableSomeComponent = Hoverable(SomeComponent, 'css-selector');

React propTypes errors not showing

I'm having problems with React propTyoes. I'v created a component that require 2 props to work as you guys can see in the code below.
When I use the component in the App file, passing just 1 prop, without the "stateSidebarVisible" it doesn't throw me any error/warning from react...
(I read a lot of things about the NODE_ENV production/development, I searched in my node for process.env and didnt found the NODE_ENV variable by the way).
Any clue?
FFMainHeader
export default class FFMainHeader extends React.Component {
render() {...}
}
FFMainHeader.propTypes = {
stateSidebarVisible: React.PropTypes.bool.isRequired,
handleSidebarChange: React.PropTypes.func.isRequired
};
App
This is where i call the FFMainHeader component.
export default class FFMainApp extends React.Component {
.......
render() {
return (
<div id="FFMainApp">
<FFMainHeader
handleSidebarChange={this.onSidebarChange} />
<FFMainSidebar />
</div>
);
}
}
EDIT
export default class FFMainHeader extends React.Component {
constructor(props) {
super(props);
this.clickSidebarChange = this.clickSidebarChange.bind(this);
}
clickSidebarChange(e) {
e.preventDefault();
(this.props.stateSidebarVisible) ?
this.props.stateSidebarVisible = false :
this.props.stateSidebarVisible = true;
this.props.handleSidebarChange(this.props.stateSidebarVisible);
}
render() {
return (
<header id="FFMainHeader">
<a href="#" onClick={this.clickSidebarChange}>
Abre/Fecha
</a>
</header>
);
}
}
FFMainHeader.propTypes = {
stateSidebarVisible: React.PropTypes.bool.isRequired,
handleSidebarChange: React.PropTypes.func.isRequired
};

How to render react component determined by string in TSX

I want to achieve navigation based on hash change in url.
for example for url index.html#HomePage the app will load HomePage component.
import { HomePage } from '../components/homepage'
import { AnotherPage } from '../components/anoterpage'
export class NavigationFrame extends React.Component<any, State> {
constructor(props) {
super(props);
this.state = { pageName: this.pageNameFromUrl() };
}
onHashTagChanged = () => {
this.setState({pageName: this.pageNameFromUrl()});
}
public render() {
var Page = this.state.pageName as any;
return <Page /> //this renders <homepage /> when this.state.pageName = "HomePage";
}
}
is there any way how to dynamically create component based on string?
class CustomComponent extends React.Component{
render(){
return (
var DynamicComponent = this.props.component;
return <DynamicComponent />;
)
}
}
import it into your file and use like below,
return (
<CustomComponent component={this.state.pageName} />
);

Resources