I have multiple buttons on my screen and and inside same container I have another label, on click I want to show the label and then hide after few seconds.
I am controlling through this.state problem is when event fires it shows all labels and then hides all. I found few solutions like assign ids etc and array for buttons.
But issue is there can be unlimited buttons so thats not the way to go to set state for each button. Or if there is any other possible way.
export default class App extends React.Component {
constructor(props) {
super();
this.state = {
visible: false
}
}
_handleClick = () => {
this.setState({
visible: !this.state.visible
});
setTimeout(() => {
this.setState({
visible: false
});
},2000);
}
render() {
return (
<div>
<button onClick={this._handleClick}>Button 1</Text></button>
{this.state.visible && <span style={styles.pic}>
Pic
</span>
}
</div>
<div>
<button onClick={this._handleClick}>Button 2</Text></button>
{this.state.visible && <span style={styles.pic}>
Pic
</span>
}
</div>
<div>
<button onClick={this._handleClick}>Button 3</Text></button>
{this.state.visible && <span style={styles.pic}>
Pic
</span>
}
</div>
</div>
);
}
}
You need each button to have its own state... So make each button a Component!
class App extends React.Component {
render() {
return <div>
<Button>1</Button>
<Button>2</Button>
<Button>3</Button>
<Button>4</Button>
</div>
}
}
class Button extends React.Component {
constructor(props) {
super();
this.state = {
visible: false
}
}
_handleClick = () => {
this.setState({
visible: !this.state.visible
});
setTimeout(() => {
this.setState({
visible: false
});
}, 2000);
}
}
If there are unlimited buttons, then you can set the state like this regarding which button is clicked.
_handleClick = (id) => {
this.setState({ [id]: true })
}
id will be the unique id of each button.
Here is a simple example to show how to set the state.
https://codesandbox.io/s/k38qyv28r
Related
I am trying to control my radio buttons with state so that i can add an active class for a radio button when it is clicked. Im stuck now because i need to double click to make the radio button change and after they changed once, i cant get anymore console output. Can someone see what is wrong?
import React, { Component } from 'react'
class CustomRadio extends Component {
constructor(props) {
super(props);
this.state = {
name:'',
id:'',
value:'',
onChange:'',
label:'',
validationMsg:'',
rbValue:'',
checked:''
};
}
onSelect = (e) => {
this.setState({
rbValue: e.target.value
});
//console.log(this.state.rbValue);
console.log(e.target.value);
console.log(this.props.checked);
}
// setClass = () => {
// if (this.state.rbChecked === true)
// return "active"
// else
// return "inactive"
// }
render() {
//let rbActiveClass = this.setClass();
return (
// <div className={`form-item custom-radio ${rbActiveClass}`}>
<div className={`form-item custom-radio`}>
{this.props.label &&
<label htmlFor={this.props.id}>
{this.props.label}
</label>
}
<input
type="radio"
id={this.props.id}
name={this.props.name}
value={this.props.value}
checked={this.state.rbValue === this.props.checked}
//defaultChecked={this.props.defaultChecked}
onChange={this.onSelect.bind(this)}
/>
{this.props.validationMsg &&
<span className="validation-message">
{this.props.validationMsg}
</span>
}
</div>
)
}
}
export default CustomRadio
just change component constructor:
class CustomRadio extends Component {
constructor(props) {
super(props);
this.state = {
//...
rbValue:props.checked,
//...
};
}
and also input component:
<input
//...
checked={this.state.rbValue}
//...
/>
Hello I am trying to render my PostOnWall component I made using an onClick function. The goal that every time someone clicks the button handleClick will render one new component on the screen each time. So if I click the button three times i should see three PostOnWall components rendered on my screen. Please tell me what I am doing wrong.
class Textbox extends Component {
constructor(props) {
super(props);
this.handleClick.bind(this);
this.state = {
textArea: "",
text: "",
show: false,
curTime : new Date().toLocaleString(),
};
}
handleChange(event) {
const myValue = event.target.value;
this.setState({
textArea: myValue
})
console.log(this.state)
}
handleClick= () => {
this.setState({text:this.state.textArea,
show: !this.state.show});
return (
<div>
{this.state.show && <PostOnWall PostOnWall={this.props.PostOnWall} text={this.state.text} time={this.state.curTime}/>}
</div>
);
}
showNewPost
render() {
return (
<div>
<textarea className="Textbox"
rows="2" cols="30"
type = "text"
onChange={this.handleChange.bind(this)}
value={this.state.textArea} >
</textarea>
<button className="postbutton" onClick={this.handleClick.bind(this)}>Post</button>
</div>
);
}
}
export default Textbox;
That should do the trick for you;
import React, { Component } from 'react';
class Textbox extends Component {
constructor(props) {
super(props);
this.handleClick.bind(this);
this.state = {
textArea: '',
text: '',
show: false,
curTime: new Date().toLocaleString(),
};
}
handleChange = (event) => {
const myValue = event.target.value;
this.setState({
textArea: myValue
});
}
handleClick= () => {
this.setState({
text: this.state.textArea,
show: !this.state.show
});
}
render() {
return (
<div>
<textarea
className="Textbox"
rows="2"
cols="30"
type="text"
onChange={this.handleChange.bind(this)}
value={this.state.textArea}
/>
<button
className="postbutton"
onClick={this.handleClick}
type="button"
>
Post
</button>
{
this.state.show &&
<PostOnWall
PostOnWall={this.props.PostOnWall}
text={this.state.text}
time={this.state.curTime}
/>
}
</div>
);
}
}
You should use the function called on click to change the state only. Then render (or not) the PostOnWall component based on the state value.
you need to add state which increase on click and then render the component depending on how many time the button is clicked. here the codeSandbox for what you are trying to achieve.
I'm trying to make a toggle content button with React. But I can only get it to open, not to close when I click on it again. Can someone please take a look and let me know what I need to change within the code to accomplish it?
Here's what I have so far:
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {
activeLocation: 0,
}
}
changeActiveLocation = (activeLocation) => {
this.setState({
activeLocation: activeLocation,
});
}
render() {
const activeLocation = company.locations[this.state.activeLocation];
return (
{company.locations.map((location, index) => (
<div className="test-item">
<div className="test-item-container" onClick={() => {this.changeActiveLocation(index)}}>
<div className="test-item-header">
<h3>Text goes here!</h3>
<a><FontAwesomeIcon icon={(this.state.activeLocation === index) ? 'times' : 'chevron-right'} /></a>
</div>
</div>
</div>
))}
)
}
}
Thank you!
You're setting the active location to be the same location that you've clicked already so the this.state.activeLocation === index is always true. I would refactor the locations to their own component with an isOpen state value that gets updated when the location is clicked. So like the following:
// test class
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {
activeLocation: 0,
}
}
changeActiveLocation = (activeLocation) => {
this.setState({
activeLocation: activeLocation,
});
}
render() {
const activeLocation = company.locations[this.state.activeLocation];
return (
{company.locations.map((location, index) => (
<LocationItem location={location} onClick={() => this.changeActiveLocation(index)} />
))}
)
}
}
// LocationItem
class LocationItem extends React.Component {
state = { isOpen: false };
handleClick = () => {
this.setState(prevState => { isOpen: !prevState.isOpen});
// call parent click to set new active location if that's still needed
if(this.props.onClick) this.props.onClick;
}
render() {
return <div className="test-item">
<div className="test-item-container" onClick={this.handleClick}>
<div className="test-item-header">
<h3>Text goes here!</h3>
<a><FontAwesomeIcon icon={(this.state.isOpen ? 'times' : 'chevron-right'} /></a>
</div>
</div>
</div>
}
}
i have a side panel with items listed. when the list item content overflows expand button appears and clicking that expand btn would show the entire content of list item
For this i have created a expandable component. this will show arrow_down when list item content overflows and clicking arrow_down shows up arrow_up.
However with the below code, clicking button 1 just makes the sidpanel disappear instead of arrow_up appearing. could some one help me solve this. thanks.
export default class Expandable extends React.PureComponent{
constructor(props) {
super(props);
this.expandable_ref = React.createRef();
this.state = {
expanded: false,
overflow: false,
};
}
componentDidMount () {
if (this.expandable_ref.current.offsetHeight <
this.expandable_ref.current.scrollHeight) {
this.setState({overflow: true});
}
}
on_expand = () => {
this.setState({expanded: true});
console.log("in expnad");
};
on_collapse = () => {
this.setState({expanded: false});
};
render () {
return (
<div className={(this.state.overflow ?
this.props.container_classname : '')}>
<div className={(this.state.overflow ?
this.props.classname : '')} style={{overflow: 'hidden',
display: 'flex', height: (this.state.expanded ? null :
this.props.base_height)}}
ref={this.expandable_ref}>
{this.props.children}
</div>
{this.state.overflow && this.state.expanded &&
<div className={this.props.expand}>
<button onClick={this.on_collapse}>
{this.props.arrow_up}</button>
</div>}
{this.state.overflow && !this.state.expanded &&
<div className={this.props.expand}>
<button onClick={this.on_expand}>
{this.props.arrow_down}</button>
</div>}
</div>
);
}
}
In the above code i pass the base_height to be 42px.
Edit:
i have realised for the side panel component i add eventlistener click to close the side panel if user clicks anywhere outside sidepanel. When i remove that eventlistener it works fine....
class sidepanel extends React.PureComponent {
constructor(props) {
super(props);
this.sidepanel_ref = React.createRef();
}
handle_click = (event) => {
if (this.sidepanel_ref.current.contains(event.target)) {
return;
} else {
this.props.on_close();
}
};
componentDidMount() {
document.addEventListener('click', this.handle_click, false);
}
componentWillUnmount() {
document.removeEventListener('click', this.handle_click, false);
}
render() {
return (
<div>
<div className="sidepanel" ref=
{this.sidepanel_ref}>
{this.props.children}
</div>
</div>
);
}
}
when i log the event.target and sidepanel_ref.current i see the button element in both of them but svg seems different in both of them.
How can i fix this?
Probably it is because click events bubble up the component tree as they do in the DOM too. If you have an element with an onClick handler inside an element with another onClick handler it will trigger both. Use event.stopPropagation() in the handler of the inner element to stop the event from bubbling up:
export default class Expandable extends React.PureComponent{
constructor(props) {
super(props);
this.expandable_ref = React.createRef();
this.state = {
expanded: false,
overflow: false,
};
}
componentDidMount () {
if (this.expandable_ref.current.offsetHeight <
this.expandable_ref.current.scrollHeight) {
this.setState({overflow: true});
}
}
toggleCollapse = event => {
// use preventDefault here to stop the event from bubbling up
event.stopPropagation();
this.setState(({expanded}) => ({expanded: !expanded}));
};
render () {
const {className, container_classname, base_height, expand, arrow_up, arrow_down} = this.props;
const {overflow, expanded} = this.state;
return (
<div className={overflow ? container_classname : ''}>
<div
className={overflow ? classname : ''}
style={{
overflow: 'hidden',
display: 'flex',
height: expanded ? null : base_height
}}
ref={this.expandable_ref}
>
{this.props.children}
</div>
{overflow && (
<div className={expand}>
<button onClick={this.toggleCollapse}>
{expanded ? arrow_up : arrow_down}
</button>
</div>
)}
</div>
);
}
}
I have this particular code that shows a list of questions and buttons for each of it. When I click the button, it will show the specific answer to the question. My problem is, I have a bunch of questions and when i click the button, it will show all of the answer instead of the specific answer to that question.
Here is the code
class App extends React.Component {
constructor(){
super()
this.state = {
answer: [],
isHidden: true
}
this.toggleHidden = this.toggleHidden.bind(this)
}
componentWillMount(){
fetch('http://www.reddit.com/r/DrunkOrAKid/hot.json?sort=hot')
.then(res => res.json())
.then( (data) => {
const answer = data.data.children.map(obj => obj.data);
this.setState({answer});
})
}
toggleHidden(){
this.setState({isHidden: !this.state.isHidden})
}
render(){
const answer = this.state.answer.slice(2)
return <div>
<h1>Drunk or Kid</h1>
{answer.map(answer =>
<div key={answer.id}>
<p className="title">{answer.title}</p>
<button onClick={this.toggleHidden}>Answer</button>
{!this.state.isHidden && <Show>{answer.selftext}</Show>}
</div>
)}
</div>
}
}
const Show = (props) => <p className="answer">{props.children}</p>
And here is the link to the codepen
Here is a Codepen based on my suggestion:
The basics of the child component would be:
class Question extends React.Component {
// Set initial state of isHidden to false
constructor() {
super();
this.state = {
isHidden: false
}
}
// Toggle the visibility
toggleHidden() {
this.setState({
isHidden: !this.state.isHidden
});
}
// Render the component
render() {
const { answer } = this.props;
return (
<div key={answer.id}>
<p className="title">{answer.title}</p>
<button onClick={ () => this.toggleHidden() }>Answer</button>
{this.state.isHidden && <Show>{answer.selftext}</Show>}
</div>
);
}
}
Then you would map over it within the parent component as:
answer.map(answer =>
<Question answer={answer} key={answer.id} />
)
Another option is to add a state that save opened answer id, then checking if specific answer in that state or not.
let's see in action
class SomeComponent extends React.Component {
constructor(props){
super(props)
this.state = {
opened: []
}
this.toggleShowHide = this.toggleShowHide.bind(this)
}
toggleShowHide(e){
const id = parseInt(e.currentTarget.dataset.id)
if (this.state.opened.indexOf(id) != -1){
// remove from array
this.setState({opened: this.state.opened.filter(o => o !== id)})
} else {
this.setState({opened: [...this.state.opened, id]})
}
}
render(){
return <ul>
{ this.state.answers.map(ans => (
<li key={ans.id} data-id={ans.id}>
question
<button onClick={this.toggleShowHide}>show answer</button>
<span
style={{ display: this.state.opened.indexOf(ans.id) !== -1 ? 'block' : 'none' }}>answer</span>
</li>
))}
</ul>
}
}
Here is video in action https://www.youtube.com/watch?v=GJsPEsckB4w