Add and remove class on react buttons - html

I'm trying to create two buttons that will change its class when they are clicked. So when button one is clicked it will add "active" class to it and remove "active" class for sibiling button.
Now I have made some progress but the thing that is not working is that I want to add active class only when the element is clicked. So by default buttons shouldn't have any classes. When then user clicks on the first button, active class will be added to that button, then if user clicks on button two, it will remove active class from button one and add it to button two. The one more thing that isn't working as expected is when I click on the already clicked and already active button it changes the class and state. So it should be that when I click on already selected button it shouldn't do anything, the button with active state should remain active. It should basically work as jQuery toggleClass function.
My react code:
import React, { Component } from "react";
import css from "./styles.css";
export default class TypeButtons extends Component {
constructor(props) {
super(props);
this.state = { isActive: true };
}
ToggleClass = (e) => {
this.setState({ isActive: !this.state.isActive })
}
render() {
return (
<div className={css.buttonsContainer}>
<button onClick={this.handleClick} className={( !this.state.isActive ? '' : 'active' )} onClick={this.ToggleClass}>
Button 1
</button>
<button onClick={this.handleClick} className={( this.state.isActive ? '' : 'active' )} onClick={this.ToggleClass}>
Button 2
</button>
</div>
);
}
}
CSS:
.active {
background: green;
}
I have created Codesandbox example: https://codesandbox.io/s/vigorous-pare-3zo0s
So long story short, class should be added only after button has been clicked, so by default both buttons shouldn't have active class at start. Also when I click button with the active class it shouldn't change the active class, it should rather remain active, the state should only be changed when the opposite button is clicked.
Any help will mean a lot!

https://codesandbox.io/s/naughty-allen-vrj3r
Here, I edited your codesandbox.
Basically what I store in the state is a key that identifies the active button. It's null by default since that's what you wanted.
I assigned integers to each button but that can be whatever you want. You could also keep the integers, and add your buttons via a .map() to have a dynamic number of buttons for examples (see Constantin's answer).

class TypeButtons extends React.Component {
constructor(props) {
super(props);
this.state = { active: "0" };
this.buttons = [
{ id: "1", title: "Button 1" },
{ id: "2", title: "Button 2" }
];
}
handleClick = e => {
this.setState({ active: e.target.dataset.id });
};
render() {
return (
<div>
{this.state.active}
{this.buttons.map(({ id, title }) => {
return (
<button
key={id}
data-id={id}
onClick={this.handleClick}
className={this.state.active === id ? "active" : ""}
>
{title}
</button>
);
})}
</div>
);
}
}

Related

How can a function value is displayed in a HTML tag with a return value from addEventListerner click?

I am trying to build a calculator and want to print digits on the screen. I have not yet put the calculator algorithm, just only to print the digits on the screen.
const Keys = ({calcKeys})=>(<div className="display-keys">
<div className="screen"><handleClick></div>
{calcKeys.map((item)=>{
return <button className="display-keys">{item.key}</button>
})
}
class App extends React.Component { constructor(props) { super(props);
this.state={calcKeys:[{"key": "AC"},{"key": "CE"},{"key": "±"},{"key": "/"},{"key": "7"},{"key": "8"},{"key": "9"},{"key": "x"},{"key": "4"},{"key": "5"},{"key": "6"},{"key": "-"},{"key": "1"},{"key": "2"},{"key": "3"},{"key": "+"},{"key": "."},{"key": "0"}]};}
this.displayKeys = this.displayKeys.bind(this)];
const keyButton = document.querySelector('.display-keys');
handleClick() {
keyButton.addEventListener('click', (e) => {
return const keyPad = e.key;
});
}
render(){
return(
<div className="display-container">
<Keys calcKeys={this.state.calcKeys}/>
</div>
);
}
}
ReactDOM.render( <App />, document.getElementById("root"));
For this case, if you want to click on the button you don't need to add an addEventListener.
As you are using React, you can create a function to handle click.
If you want to handle a keypress on the keyboard, that's the case to use addEventListener.
I changed your code a bit in order to make it work as expected. I didn't add any logic to make the calculator work but clicking on any button will add it to state and display on "screen".
This is what I did:
// "Keys" Component receives the calcKeys and the handleClick function.
// It uses the handleClick function on the button onClick passing the current item key
const Keys = ({ calcKeys, handleClick }) => (
<div className="display-keys">
{calcKeys.map(item => (
<button onClick={() => handleClick(item.key)}>{item.key}</button>
))}
</div>
)
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
calcKeys: [{"key": "AC"},{"key": "CE"},{"key": "±"},{"key": "/"},{"key": "7"},{"key": "8"},{"key": "9"},{"key": "x"},{"key": "4"},{"key": "5"},{"key": "6"},{"key": "-"},{"key": "1"},{"key": "2"},{"key": "3"},{"key": "+"},{"key": "."},{"key": "0"}],
value: '',
};
this.handleClick = this.handleClick.bind(this)
}
// Here I just receive the key and add it to state.
// This is the place to add logic, check if the key is "AC" for example and clean the state, etc.
handleClick(key) {
const { value } = this.state
this.setState({ value: `${value}${key}` })
}
render() {
const { value } = this.state
return (
<div className="display-container">
<div className="screen">{value}</div>
<Keys calcKeys={this.state.calcKeys} handleClick={this.handleClick} />
</div>
);
}
}
You can test it in a working JSFiddle here

React js how to show clicked div and delete previous div content

Hello i have a question if some one could help.
I created a react app with tree buttons when i click on every button the code shows and hide text.
But i wanted when i click for example on button 2 the text from button 1 and 3 to be hidden. And the same for every button if i click on button 3 the text from button 1 and 2 to be hidden also.
import React, { useState } from 'react';
export default class Tinfo extends React.Component{
constructor(props){
super(props);
this.state = { show: new Array(3).fill(false) };
this.baseState = this.state
}
resetState = () => {
this.setState(this.baseState)
}
toggleDiv = (index) => {
var clone = Object.assign( {}, this.state.show );
switch(clone[index]){
case false:
clone[index] = true
break;
case true:
clone[index] = false
break;
}
this.setState({ show: clone });
}
render(){
return(
<div>
{ this.state.show[0] && <div id="tinfo"> First Div </div>}
{ this.state.show[1] && <div id="tinfo"> Second Div </div>}
{ this.state.show[2] && <div id="tinfo"> Third Div </div> }
<button onClick={() => this.toggleDiv(0)}>button 1</button>
<button onClick={() => this.toggleDiv(1)}>button 2</button>
<button onClick={() => this.toggleDiv(2)}>button 3</button>
</div>
)
}
}
since only one can be shown, then just reset the state
toggleDiv = index => {
const show = new Array(3).fill(false);
show[index] = true;
this.setState({
show
});
}
although this should now be named showDiv as it sets the state and hides the rest, it's not a toggle.

How to hide a div and replace it with another div in reactjs?

There are 2 divs. div1 and div2.
Initialy, div1 is shown and div2 is hidden.
Onclick of a button, div1 has to be hidden and div2 should be displayed in the place of div1.
Create a state to indicate whether div1 is to be shown or div2 is to be shown. Then, add a onClick handler function to the button. Finally, conditionally render which component is to be shown according to that state.
Code:
class TwoDivs extends React.Component {
state = {
div1Shown: true,
}
handleButtonClick() {
this.setState({
div1Shown: false,
});
}
render() {
return (
<div>
<button onClick={() => this.handleButtonClick()}>Show div2</button>
{
this.state.div1Shown ?
(<div className="div1">Div1</div>)
: (<div className="div2">Div2</div>)
}
</div>
);
}
}
You can achieve it by adding a new property inside component's state. Clicking the button will simply toggle that state, and the component will re-render, due to setState method. Please notice that this will toggle between the two divs. If you only want to show the second one, set the new state like this: this.setState({firstDivIsActive: false}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
firstDivIsActive: true
};
}
render() {
let activeDiv;
if (this.state.firstDivIsActive) {
activeDiv = <div>I am one</div>;
} else {
activeDiv = <div>I am two</div>;
}
return (
<div>
<button
onClick={() => {
this.setState({
firstDivIsActive: !this.state.firstDivIsActive
});
}}
>
Toggle Div
</button>
{activeDiv}
</div>
);
}
}
I would have done like below in simple HTML and JQuery as below:
<input id="DivType" type="hidden" value="div1" class="valid" />
<div id="div1" />
<div id="div2" />
function ToggleDiv() {
var divType = $("#DivType").val();
if (divType === "div1") {
$("#div1").show();
$("#div2").hide();
$('#DivType input').value = "div2";
}
else if (divType === "div2") {
$("#div1").show();
$("#div2").hide();
$('#DivType input').value = "div1";
}
}
ToggleDiv will be called on OnClick event of button.
Not sure whether there is any better way to do it in React.
class ToggleDivs extends React.Component {
state = {
showDiv1: true,
}
handleButtonClick() {
this.setState({
showDiv1: !this.state.showDiv1
});
}
render() {
const { showDiv1 } = this.state;
const buttonTitle = showDiv1 ? 'Div2' : 'Div1';
const div1 = (<div className="div1">Div1</div>);
const div2 = (<div className="div2">Div2</div>);
return (
<div>
<button onClick={() => this.handleButtonClick()}>Show {buttonTitle}</button>
{showDiv1 ? div1 : div2}
</div>
);
}
}

ReactJS component textarea not updating on state change

I'm trying to write a note taking/organizing app and I've run into a frustrating bug.
Here's my component:
import React from 'react';
const Note = (props) => {
let textarea, noteForm;
if (props.note) {
return (
<div>
<button onClick={() => {
props.handleUpdateClick(props.selectedFolderId, props.selectedNoteId, textarea.value);
}}>
Update
</button>
<textarea
defaultValue={props.note.body}
ref={node => {textarea = node;}}
/>
</div>
);
} else {
return <div></div>;
}
};
export default Note;
As it currently stands, whenever I switch between notes and rerender the note component with new content from the note.body prop, the textarea does not change and retains the content from the previous note. I've tried using the value attribute instead of the defaultValue attribute for the text area which doe fix the problem of the text area content not changing when the component rerenders, but when I do that I'm longer able to type in the textarea field to update the note
Doe anyone know a way I can both allow for users to type in the text field to update the note as well as have the textarea content change when I render different notes?
Thank you
The problem is that setting the value to your prop will cause all re-renders of the component to use the same prop, so new text is obliterated. One solution is to preserve the text in the local state of the component. To simultaneously listen to prop changes, you can set the state when you receive new props.
const Note = React.createClass({
getInitialState() {
return {
text : this.props.note.body
}
},
componentWillReceiveProps: function(nextProps) {
if (typeof nextProps.note != 'undefined') {
this.setState({text: nextProps.note.body });
}
},
render() {
if (this.props.note) {
return (
<div>
<button onClick={(e) => {
// Fire a callback that re-renders the parent.
// render(this.textarea.value);
}}>
Update
</button>
<textarea
onChange={e => this.setState({ text : e.target.value })}
value={this.state.text}
ref={node => {this.textarea = node;}}
/>
</div>
);
} else {
return <div></div>;
}
}
});
https://jsfiddle.net/69z2wepo/96238/
If you are using redux, you could also fire an action on the change event of the input to trigger a re-render. You could preserve the input value in a reducer.
Because componentWillReceiveProps is now unsafe Max Sindwani's answer is now a little out date.
Try these steps:
convert your component to a class
now you can include the shouldComponentUpdate() lifecycle hook
create your event handler and pass it into onChange
in <textarea> you can swap out defaultValue attribute for value (just use event.preventDefault() in the handler so that a user can continue to update text if required)
import React from 'react';
export class Note extends React.Component {
constructor(props) {
super(props);
this.state={text: this.props.note.body}
}
shouldComponentUpdate(nextProps) {
if(nextProps.note.body !== this.state.text) {
this.setState({text: nextProps.note.body})
return true;
}
return false;
}
updateText = (event) => {
event.preventDefault();
this.setState({text: nextProps.note.body});
}
render() {
if (this.props.note) {
return (
<div>
<textarea
onChange={this.updateText}
value={this.state.text}
name={'display'}
/>
</div>
);
} else {
return <div></div>;
}
}});

How to recognise which reactjs component I am clicking on?

I am trying to trigger an event for my reactjs component when it is outside it. Currently I have a collapsible div (blue background) that I want to close once the user clicks outside of it. I have an method pageClick in it to log the event but I can't find a property to use:
componentDidMount() {
window.addEventListener('mousedown', this.pageClick, false)
}
pageClick(e) {
console.log('testing=pageClick', e)
}
How can I detect whether I am on the component with the collapseclass or not so I can change the state of it?
codepen here
You can check the class of the clicked element to know if it belongs to your collapsible element
componentDidMount() {
window.addEventListener('mousedown', this.pageClick.bind(this), false)
// ^^^^^^^^^^
// bind your function pageClick to this so you can call setState inside
}
pageClick(e) {
const el = e.target;
if (e.target.classList.contains('blue')) {
this.setState({ open: false });
}
}
But this is a poor solution because if you have many different DOM nodes in your collapsible element e.target will be the element below the mouse, not the parent .collapse element.
So I suggest you to use a library to detect the click outside your element : react-onclickoutside do the job perfectly.
You can see an implementation of your use case using react-click-outside in this fiddle.
You can listen for click event on the document like this -
document.addEventListener("click", this.closeComponent);
As an example you can define your collapsible component like this -
import React, { Component } from 'react';
export default class CollapsibleComponent extends Component {
constructor(props) {
super(props);
this.state = {
style : {
width : 350
}
};
this.showComponent = this.showComponent.bind(this);
this.closeComponent = this.closeComponent.bind(this);
}
componentDidMount() {
document.addEventListener("click", this.closeComponent);
}
componentWillUnmount() {
document.removeEventListener("click", this.closeComponent);
}
showComponent() {
const style = { width : 350 };
this.setState({ style });
document.body.style.backgroundColor = "rgba(0,0,0,0.4)";
document.addEventListener("click", this.closeComponent);
}
closeComponent() {
document.removeEventListener("click", this.closeComponent);
const style = { width : 0 };
this.setState({ style });
}
render() {
return (
<div
id = "myCollapsibleComp"
ref = "ccomp"
style = {this.state.style}
>
<div className = "comp-container">
<a
href = "javascript:void(0)"
className = "closebtn"
onClick = {this.closeComponent}
>
×
</a>
</div>
</div>
);
}
}