How do I replace href in a button with onClick? - react-typescript

I am new to Typescript, but I have a button that I want to add an onClick to. Right now there is a href tag in the button, and I want to remove the href and replace it with an onClick that will perform the action
<PrimaryButton
className={}
href={}
target={}
>
{/* some code */}
</PrimaryButton>

If you want to have an onClick prop do this
function PrimaryButton({ onButtonClick }) {
return (
<button onClick={onButtonClick}>
// button content
</button>
)
}
export default PrimaryButton
When you use the component just pass a function / or state modifier as a value of your onButtonClick prop.
import PrimaryButton from './components/Buttons/PrimaryButton'
function Home() {
function handlePrimaryButtonClick() {
// Do whatever you want to do on PrimaryButton click
}
return (
<PrimaryButton onButtonClick={() => handlePrimaryButtonClick()}
)
}

Related

How to change / toggle React state?

I am trying to toggle react state after the button click. After clicking button Work From Office should change to work From Home and vice versa. But it is not working. What am I dong wrong? I am able to change only once. Can we do with if statement? What is simple way?
** React **
import React, { Component } from 'react';
import './ChangeSchedule.css';
class ChangeSchedule extends Component {
constructor(){
super()
this.state = {
// work:'from office'
workFromOffice:true
}
}
changeMyWorkPlace(){
this.setState({
// work:'from Home'
workFromOffice:!this.state.workFromOffice
})
}
render(){
return(
<div>
<div class="schedule change">
<h3>Emplyoee Name: </h3>
<p>Today Pooja is work {this.state.work}</p>
{/* <button class="chageScheduleBtn " onClick = {()=> this.changeMyWorkPlace()}> Change My Schedule </button> */}
<button class="chageScheduleBtn " onClick = {()=> this.workFromOffice() ?'Home': 'Office'}> Change My Schedule </button>
</div>
</div>
)
}
}
export default ChangeSchedule;
You can use a ternary expression to display content for each state.
For example:
{this.state.workFromOffice ? " from Office" : " from Home"}
Now this button should work as you expect:
<button class="chageScheduleBtn" onClick={()=> this.changeMyWorkPlace()}>
Change My Schedule
</button>
See codesandbox for fully working example
You could do it as below. Just change the status when the click happen. And inside the button, use a ternary expression. Like so:
import { Component } from "react";
import "./ChangeSchedule.css";
class ChangeSchedule extends Component {
constructor() {
super();
this.state = {
// work:'from office'
workFromOffice: true,
};
}
changeMyWorkPlace() {
this.setState({
// work:'from Home'
workFromOffice: !this.state.workFromOffice,
});
}
render() {
return (
<div>
<div class="schedule change">
<h3>Emplyoee Name: </h3>
<p>Today Pooja is work {this.state.work}</p>
<button class="chageScheduleBtn " onClick={() => this.workFromOffice()}>
{this.state.workFromOffice ? "Work From Home" : "Work From Office"}
</button>
</div>
</div>
);
}
}
export default ChangeSchedule;
The answer is in the way you're structuring your state. You can make it really simple by just using one entry of the state - workFromOffice. Then, your click handler should care only about changing that state value to the opposite of what was set before. Example:
onClick={() => this.setState({ workFromOffice: !this.state.workFromOffice })}
When the changeMyWorkPlace function created, it captures your initial state and uses it everytime you run the function so only works once. You should instruct react to use up to date state.
try this way.
changeMyWorkPlace(){
this.setState((previousState) => ({
// work:'from Home'
workFromOffice:!previousState.workFromOffice
}))
}

Add a class to the HTML <body> tag with React?

I'm making a modal in my React project that requires a class to be added to the body when the modal is open and removed when it is closed.
I could do this the old jQuery way by running some vanilla JavaScript which adds / removes a class, however this doesn't feel like the normal React philosophy.
Should I instead setState on my top level component to say whether the modal is open or closed? Even if I did this, as it's rendered into the div on the page it's still a side-effect to edit the body element, so is there any benefit for this extra wiring?
TL;DR use document.body.classList.add and document.body.classList.remove
I would have two functions that toggle a piece of state to show/hide the modal within your outer component.
Inside these functions I would use the document.body.classList.add and document.body.classList.remove methods to manipulate the body class dependant on the modal's state like below:
openModal = (event) => {
document.body.classList.add('modal-open');
this.setState({ showModal: true });
}
hideModal = (event) => {
document.body.classList.remove('modal-open');
this.setState({ showModal: false });
}
With the new React (16.8) this can be solved with hooks:
import {useEffect} from 'react';
const addBodyClass = className => document.body.classList.add(className);
const removeBodyClass = className => document.body.classList.remove(className);
export default function useBodyClass(className) {
useEffect(
() => {
// Set up
className instanceof Array ? className.map(addBodyClass) : addBodyClass(className);
// Clean up
return () => {
className instanceof Array
? className.map(removeBodyClass)
: removeBodyClass(className);
};
},
[className]
);
}
then, in the component
export const Sidebar = ({position = 'left', children}) => {
useBodyClass(`page--sidebar-${position}`);
return (
<aside className="...">
{children}
</aside>
);
};
Actually you don't need 2 functions for opening and closing, you could use document.body.classList.toggle
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
document.body.classList.toggle('modal-open', isOpen);
},[isOpen])
<button onCLick={()=> setIsOpen(!isOpen)}>Toggle Modal</button>
Like what #brian mentioned, try having a top-level container component that wraps around your other components. (assuming you're not using redux in your app)
In this top-level component:
Add a boolean state (eg. modalOpen) to toggle the CSS class
Add methods (eg. handleOpenModal & handleCloseModal) to modify the boolean state.
Pass the methods created above as props into your <Modal /> component
ReactJS has an official React Modal component, I would just use that: https://github.com/reactjs/react-modal

React onChange handler is being called multiple times during page load

We're trying to add a onChange handler to one of our custom components - namely, a Checkbox component (the only reason for it being a custom component is so that we can efficiently encapsulate the intermediate HTML attribute). It looks something like this:
<Checkbox
id="select-all"
onChange={this.handleSelectAllChange(ids)}
indeterminate={isIndeterminate}
checked={areVisibleItemsSelected}
disabled={isDisabled}
/>
The handler function is structured somewhat like this:
handleSelectAllChange(ids) {
// omitted code that filters on ids and produces newIds
this.props.updateIds(newIds);
}
Where this.props.updateIds is a passed-down function that modifies the parent component's state.
The problem is that this function is called about 10 times during page load, which is not intended. I thought it was only called when the actual checkbox element is modified?
By declaring it like this onChange={this.handleSelectAllChange(ids)} the method call happens immediately at rendering the CheckBox. With ES6 you can avoid this by using
onChange={() => this.handleSelectAllChange(ids)}
This means you pass a new function which will call handleSelectAllChange on change.
I just had the same issue... I was able to fix the problem stopping the propagation of the event.
Add this in the function being called by your onChange event:
e.stopPropagation();
Pass the handler function like
<Checkbox
id="select-all"
onChange={this.handleSelectAllChange.bind(this,ids)}
indeterminate={isIndeterminate}
checked={areVisibleItemsSelected}
disabled={isDisabled}
/>
You should define onClick inside the element itself, and pass a pointer to the handler function:
function Checkbox(props) {
return (<input type="checkbox" value={props.value} key={props.value}
onClick={props.clickHandler} />); // actual onclick
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
boxes: ['1', '2', '3'],
}
}
handleSelectAllChange(box) {
console.log(box)
}
render() {
const boxes = this.state.boxes.map((b,i) =>
<Checkbox value={b}
clickHandler={this.handleSelectAllChange.bind(this, b)} // pass click handler
key={i}/>
);
return (
<div>
{boxes}
</div>
);
}
}
ReactDOM.render(<App/>,
document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app">Loading...</div>
this.handleSelectAllChange(ids) means call the function.
You should pass a function object to event handlers. e.g.()=>{this.handleSelectAllChange(ids)}

React OnClick Function not Binding

I have an onClick handler that is bound to currentAreas component. The onClick handler is called simultaneously when the currentAreas component is called, but does not work after that.
onClick does not work when I try to click the anchor tag and I think it might be due to the way I'm binding the onClick function.
currentAreas.js
import React, { Component, PropTypes } from 'react';
export default class currentAreas extends Component {
constructor(props) {
super(props);
}
render() {
const { onClick } = this.props;
return(
<div className="list-group panel">
<a href="#demo3" className="list-group-item" onClick={onClick("All", "All")} >All</a>
</div>
);
}
}
currentList.js (Main component)
class currentList extends Component {
constructor(props) {
super(props);
this.updateLocation = this.updateLocation.bind(this);
}
updateLocation(name, nameLocation){
console.log(name);
console.log(nameLocation);
}
render() {
return (
<div className="step-3-container">
<CurrentAreas
onClick ={this.updateLocation} />
</div>
)
}
}
Whenever your javascript interpreter "sees" a "()" it tries to execute the function that is before "()". Therefore, what your code is actually doing is executing onClick (the one that came from props), passing as arguments "All" and "All". So, what you need to do is, instead of calling it yourself, let the onClick handler call it.
In other words, the onClick prop must receive a function.
One possible solution is to wrap your onClick into an arrow function, doing something like that:
<a href="#demo3" onClick={() => onClick("All", "All")}>All</a>
Or, you may bind the parameters to your function (bind returns a function, so it will fit well on onClick).
<a href="#demo3" onClick={onClick.bind(null, "All", "All")}>All</a>
The first bind parameter is the this value inside the binded function.
FMI about bind: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
Youre executing the function during the declaration of the component, which likely means youre triggering it on render, not click
onClick={onClick("All", "All")}
needs to be
onClick={onClick.bind(null, "All", "All")}
or
onClick={function(){ onClick("All", "All"); }}
The onClick of a react component needs a callback that can be executed. you are passing in the return value of this.props.onClick because youre calling it right away with arguments

How can I access a hover state in reactjs?

I have a sidenav with a bunch of basketball teams. So I would like to display something different for each team when one of them is being hovered over. Also, I am using Reactjs so if I could have a variable that I could pass to another component that would be awesome.
React components expose all the standard Javascript mouse events in their top-level interface. Of course, you can still use :hover in your CSS, and that may be adequate for some of your needs, but for the more advanced behaviors triggered by a hover you'll need to use the Javascript. So to manage hover interactions, you'll want to use onMouseEnter and onMouseLeave. You then attach them to handlers in your component like so:
<ReactComponent
onMouseEnter={() => this.someHandler}
onMouseLeave={() => this.someOtherHandler}
/>
You'll then use some combination of state/props to pass changed state or properties down to your child React components.
ReactJs defines the following synthetic events for mouse events:
onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit
onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave
onMouseMove onMouseOut onMouseOver onMouseUp
As you can see there is no hover event, because browsers do not define a hover event natively.
You will want to add handlers for onMouseEnter and onMouseLeave for hover behavior.
ReactJS Docs - Events
For having hover effect you can simply try this code
import React from "react";
import "./styles.css";
export default function App() {
function MouseOver(event) {
event.target.style.background = 'red';
}
function MouseOut(event){
event.target.style.background="";
}
return (
<div className="App">
<button onMouseOver={MouseOver} onMouseOut={MouseOut}>Hover over me!</button>
</div>
);
}
Or if you want to handle this situation using useState() hook then you can try this piece of code
import React from "react";
import "./styles.css";
export default function App() {
let [over,setOver]=React.useState(false);
let buttonstyle={
backgroundColor:''
}
if(over){
buttonstyle.backgroundColor="green";
}
else{
buttonstyle.backgroundColor='';
}
return (
<div className="App">
<button style={buttonstyle}
onMouseOver={()=>setOver(true)}
onMouseOut={()=>setOver(false)}
>Hover over me!</button>
</div>
);
}
Both of the above code will work for hover effect but first procedure is easier to write and understand
I know the accepted answer is great but for anyone who is looking for a hover like feel you can use setTimeout on mouseover and save the handle in a map (of let's say list ids to setTimeout Handle). On mouseover clear the handle from setTimeout and delete it from the map
onMouseOver={() => this.onMouseOver(someId)}
onMouseOut={() => this.onMouseOut(someId)
And implement the map as follows:
onMouseOver(listId: string) {
this.setState({
... // whatever
});
const handle = setTimeout(() => {
scrollPreviewToComponentId(listId);
}, 1000); // Replace 1000ms with any time you feel is good enough for your hover action
this.hoverHandleMap[listId] = handle;
}
onMouseOut(listId: string) {
this.setState({
... // whatever
});
const handle = this.hoverHandleMap[listId];
clearTimeout(handle);
delete this.hoverHandleMap[listId];
}
And the map is like so,
hoverHandleMap: { [listId: string]: NodeJS.Timeout } = {};
I prefer onMouseOver and onMouseOut because it also applies to all the children in the HTMLElement. If this is not required you may use onMouseEnter and onMouseLeave respectively.
This won't work for OP because they wanted a variable but for those who just want a UI hover effect it's usually easier to stick with CSS.
Below example will reveal a delete button when an item is hovered over:
<div className="revealer">
<div>
{itemName}
</div>
<div className="hidden">
<Btn label="Delete"/>
</div>
</div>
.hidden {
display: none;
}
.revealer:hover .hidden {
display: block;
}
Parent div has revealer class. When it's hovered over, it'll reveal the hidden div. Hidden div must be nested inside revealer div.
You can implement your own component logics using those events which stolli and BentOnCoding suggested above, or use the module named react-hover
if I could have a variable that I could pass to another component that would be awesome.
then you can simply wrap another component
<ReactHover options={optionsCursorTrueWithMargin}>
<Trigger type="trigger">
<TriggerComponent />
</Trigger>
<Hover type="hover">
<HoverComponent />
</Hover>
</ReactHover>
or your plain HTML:
<ReactHover options={optionsCursorTrueWithMargin}>
<Trigger type="trigger">
<h1 style={{ background: '#abbcf1', width: '200px' }}> Hover on me </h1>
</Trigger>
<Hover type="hover">
<h1> I am hover HTML </h1>
</Hover>
</ReactHover>
demo code here: demo
Using useState,
import React, { useState } from "react";
function App() {
const [ishover,sethover]=useState(false);
function MouseOver() {
sethover(true);
}
function MouseOut() {
sethover(false);
}
return (
<div>
<button
style={{backgroundColor: ishover?"black":null}}
onMouseOver={MouseOver}
onMouseOut={MouseOut}
onClick={handleClick}
>
Submit
</button>
</div>
);
}
export default App;
You can try to implement below code. Hover functionality can be acheived with Tooltip.
Please refer below code and link for clarity
https://mui.com/material-ui/react-tooltip/
import * as React from 'react';
import DeleteIcon from '#mui/icons-material/Delete';
import IconButton from '#mui/material/IconButton';
import Tooltip from '#mui/material/Tooltip';
export default function BasicTooltip() {
return (
<Tooltip title="Delete">
<IconButton>
<DeleteIcon />
</IconButton>
</Tooltip>
);
}