I am new to react - I'm sure this is a simple problem but I am having a lot of trouble figuring out a solution.
So I have two buttons. Onclick on first button it should open fade in and if i click on the same button(button1) or if I click on the button(button2) it should fade out. same for the button2.
so far I managed to figure it out how fade in works but not able to work out on fade out.
import React, { Component } from 'react'
import { Jumbotron, Grid, Row, Col, Image, Button, Carousel,Fade,Well } from 'react-bootstrap';
import './About.css';
export default class About extends Component {
constructor(props, context) {
super(props, context);
this.state = {
buttonPressed: false,
buttonPressed1: false
//open: false
}
}
handleClick = (button) => {
this.setState({ buttonPressed: button })
}
handleClick1 = (button) => {
this.setState({ buttonPressed1: button })
}
render() {
return (
<Grid>
<button className='button1' onClick={() => this.handleClick({ open: !this.state.buttonPressed })}>
BUTTON 1
</button>
<button className='button2' onClick={() => this.handleClick1({ open: !this.state.buttonPressed1 })}>
BUTTON 2
</button>
<Fade class='fade1' in={this.state.buttonPressed}>
<div>
<Well>
first
</Well>
</div>
</Fade>
<Fade class='fade2' in={this.state.buttonPressed1}>
<div>
<Well>
second
</Well>
</div>
</Fade>
</Grid>
)
}
}
Please help with the about code.
Thanks in advance.
First screenshot
Second screnshot
You are passing an object not a boolean value to handleClick method. Here's how it should be:
handleClick = (button) => {
this.setState({ buttonPressed: button.open })
}
handleClick1 = (button) => {
this.setState({ buttonPressed1: button.open })
}
Related
I am building a carousel component in react -- and the client wants there to be an autoscroll option - my concern though is interfering with the user interface -- what kind of logic to build to break/pause the autoscroll without interfering with the user trying to use it in the first place. So hover detection is needed?
//Original Carousel
https://codesandbox.io/s/holy-https-h0yv3
//With current autoscroll option on - but ignoring all UI considerations
https://codesandbox.io/s/nervous-brown-2inki?file=/src/Carousel/Carousel.js
also - the carousel after going through the slides - will just ZIP through the deck - without being able to snap to the slide - so that's another issue
import React, { Component } from 'react';
import Button from '#material-ui/core/Button';
import ChevronLeftIcon from '#material-ui/icons/ChevronLeft';
import ChevronRightIcon from '#material-ui/icons/ChevronRight';
import './Carousel.scss';
class Carousel extends Component {
constructor() {
super();
this.myRef = React.createRef();
this.state = {
currentSlide: 0,
slideWidth: 0,
slideCount: 0
};
this.updateDimensions = this.updateDimensions.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.prev = this.prev.bind(this);
this.next = this.next.bind(this);
}
componentDidMount() {
window.addEventListener("resize", this.updateDimensions);
this.updateDimensions();
this.setState({
slideCount: this.props.items.length
});
this.autoSlide();
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateDimensions);
}
handleKeyPress(e) {
if (e.keyCode === 39) {
this.next();
}
if (e.keyCode === 37) {
this.prev();
}
}
updateDimensions(){
//update the dimensions of the slide for responsiveness
this.setState({
slideWidth: this.myRef.current.getBoundingClientRect().width
});
}
prev(){
//if the current slide won't hit negative slide - change current slide
if(this.state.currentSlide > 0){
let prevSlide = this.state.currentSlide-1;
this.setState({
currentSlide: prevSlide
});
}
}
next(){
//if the next slide will not go beyond the items - change current slide
if(this.state.currentSlide < this.props.items.length-1){
let nextSlide = this.state.currentSlide+1;
this.setState({
currentSlide: nextSlide
});
}
}
selected(slide){
console.log(slide)
this.setState({
currentSlide: slide
});
}
autoSlide(){
let that = this;
let count = this.props.items.length;
let i = 0;
setInterval(function(){
that.selected(i);
i++;
if(count === i){
i=0;//resets
}
}, 3000);
}
render() {
return (
<div ref={this.myRef} className="carousel" onKeyDown={this.handleKeyPress} tabIndex={-1}>
<div className="carousel-holder">
<div className="carousel-wrapper">
<div
className="slide-wrapper"
style={{
left: -this.state.slideWidth*this.state.currentSlide,
width: this.state.slideWidth*this.state.slideCount
}}
>
{
this.props.items.map((item, j) => {
return(
<div
key={j}
className="slide"
style={{
width: this.state.slideWidth
}}
>
<div className={"gutter " + (this.props.hasGutter? "has-gutter" : "")}>
{item.slide}
</div>
</div>
)
})
}
</div>
</div>
<Button
className="carousel-arrow prev-arrow"
onClick={this.prev}
disabled={(this.state.currentSlide === 0)}
>
<ChevronLeftIcon />
</Button>
<Button
className="carousel-arrow next-arrow"
onClick={this.next}
disabled={(this.state.currentSlide === this.props.items.length-1)}
>
<ChevronRightIcon />
</Button>
</div>
<div className="carousel-dots">
<ul className="dot-wrapper">
{
this.props.items.map((item, x) => {
return (
<li
key={x}
className={(this.state.currentSlide === x? "selected" : "")}
>
<Button
className="dot"
onClick={() => this.selected(x)}
/>
</li>
)
})
}
</ul>
</div>
</div>
);
}
}
export default Carousel;
You should create a boolean shouldAutoScroll which should be passed as prop to this Carousel component. Initially, it's value would be true, which means this slides should autoscroll, based on a time-interval you can keep changing the active slide.
One the user hovers over the images or once the user clicks the next/previous button, set that shouldAutoScroll to false, which would mean that logic to autoscroll should be stopped.
this.state = {
shouldAutoScroll: true,
...
}
and since you are already calling it on componentDidMount
componentDidMount() {
...
this.state.shouldAutoScroll && this.autoSlide();
}
Toggle it's value, on the next() and prev(), where it is certain that user wants to take control of the scrolling.
next/prev () {
this.setState(p => p.shouldAutoScroll && ({...p, shouldAutoScroll: false}));
... // further logic
}
I'm not sure how to adjust your code in the given scenario according to your expectation. But I also wanted the same thing to be done in my carousel. Then I found this npm package that can be used to display a carousel. I think you also can use this and rectify your problem.
I have a problem with react-bootstrap-sweetalert library in react. Actually it works fine, untill slow internet connection. When someone tries to click submit button, because of the slow internet (I'm simulating it through "Network section [Slow 3G]") alert is not closing exactly at time after clicking a button, but after several seconds. So, there is probability that someone can click several times submit button. It is a problem, because several same requests can flow to backend and database. In other sections without using a library I can just "disable" react states after handling onClick.
So question is - to disable button in react-bootstrap-sweetalert library after handling onConfirm function.
Code:
handleSubmitInvoice = () => {
this.setState({
sweetalert: (
<SweetAlert
warning
showCancel
confirmBtnText={this.state.alert.label.sure}
cancelBtnText={this.state.alert.label.cancel}
confirmBtnBsStyle="success"
cancelBtnBsStyle="default"
disabled={disableButton}
title={this.state.alert.label.areyousure}
onConfirm={() => this.submit()}
onCancel={() => this.hideAlert()}
>
{this.state.alert.confirmSubmit}
</SweetAlert>
)
});
};
in render():
<button
className="btn btn-success btn-sm"
onClick={this.handleSubmitInvoice}
>
submit
</button>
submit function:
submit = () => {
const req = { invoice: this.state.invoiceNumber };
Axios.post("/api", req)
.then(() => {
this.props.history.push({
pathname: "/mypathname",
state: {
fromSubmitInvoice: true
}
});
})
.catch(err => {
Alert.error(
err.response.data.code === "internal_error"
? this.state.alert.raiseError
: err.response.data.text,
{
position: "top-right",
effect: "bouncyflip",
timeout: 2000
}
);
this.hideAlert();
});
};
Codesandbox: https://codesandbox.io/s/sweet-alert-problem-ktzcb
Thanks in advance.
Problem solved try this out
import React, { Component } from "react";
import SweetAlert from "react-bootstrap-sweetalert";
import ReactDOM from "react-dom";
const SweetAlertFunction = ({ show, disableButton, submit, hideAlert }) => {
return (
<SweetAlert
warning
show={show}
showCancel
confirmBtnText="confirmBtnText"
cancelBtnText="cancelBtnText"
confirmBtnBsStyle="success"
cancelBtnBsStyle="default"
disabled={disableButton}
title="title"
onConfirm={submit}
onCancel={hideAlert}
>
submit
</SweetAlert>
);
};
export default class HelloWorld extends Component {
constructor(props) {
super(props);
this.state = {
disableButton: false,
show: false
};
}
hideAlert() {
this.setState({
show: false
});
}
submit() {
this.setState({ disableButton: true });
console.log("submit");
setTimeout(() => {
this.setState({ disableButton: false });
}, 3000);
}
render() {
const { show, disableButton } = this.state;
console.log("disableButton", disableButton);
return (
<div style={{ padding: "20px" }}>
<SweetAlertFunction
show={show}
disableButton={disableButton}
submit={() => this.submit()}
hideAlert={() => this.hideAlert()}
/>
<button
className="btn btn-success btn-sm"
onClick={() => this.setState({ show: true })}
>
Click
</button>
</div>
);
}
}
ReactDOM.render(<HelloWorld />, document.getElementById("app"));
In your case, since you are assigning the Sweetalert component to the sweetalert state, you need to have a local state that controls the disabled state, but to make it simple, you can make sweetalert state control the visibility/presence of the Sweetalert component, like below:
handleSubmitInvoice() {
// just set sweetalert to true to show the Sweetalert component
this.setState({ sweetalert: true });
}
render() {
const { sweetalert, disableButton } = this.state;
return (
<div style={{ padding: "20px" }}>
// this makes disableButton reactive and pass it automatically to Sweetalert component
{sweetalert && (
<SweetAlert
warning
showCancel
confirmBtnText="confirmBtnText"
cancelBtnText="cancelBtnText"
confirmBtnBsStyle="success"
cancelBtnBsStyle="default"
disabled={disableButton}
title="title"
onConfirm={() => this.submit()}
onCancel={() => this.hideAlert()}
>
submit
</SweetAlert>
)}
<button
className="btn btn-success btn-sm"
onClick={this.handleSubmitInvoice}
>
Click
</button>
</div>
);
}
You can see it in this sandbox https://codesandbox.io/s/sweet-alert-problem-lv0l5
P.S. I added setTimeout in submit to make disabling of button noticeable.
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 am building an app in React, that is connected to an API I have written before. Buttons are renderizing but all of them change at the same time. I need advice about how can I write my code in order to separate the functionality.
My app renderize with a .map the same number of Buttons as appointments which is an array. All of them change when this.state.shown change but I need to separate all the buttons in order to only show the one that I clicked. Right now, when I clicked in one of them, this.state.shown change its value so all the buttons change because all depends of the same variable. I am looking for advices about how I can separate this.
class AppointmentsList extends Component {
constructor(props) {
super(props);
this.state = {
appointments: [],
isLoading: false,
shown: false, //Variable to know if a button need to change and render the component
customerUp: false
}
this.toggleCustomer = this.toggleCustomer.bind(this);
//this.showCustomer = this.showCustomer.bind(this);
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
How can I reorganize my code in order to render the Buttons separately?
Thanks in advance.
Method 1: You can make shown state a object like:
state = {
shown:{}
}
toggleCustomer(id) {
const updatedShownState = {...this.state.shown};
updatedShownState[id] = updatedShownState[id] ? false : true;
this.setState({
shown: updatedShownState,
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown[app.customer.id] ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer(app.customer.id) }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown[app.customer.id] ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
Method 2: Make a separate component for Button and Customer
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<Appointment key = {app.id} app = {app} />
)}
</div>
)
}
class Appointment extends Component {
state = {
shown: false,
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
}
render() {
const { app } = this.props;
return (
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)
}
}
Let me know if it works and the method you prefer.
You can create a separate component for your button (buttonComponent) inside your AppointmentsList component and pass the shown has props and the in componentDidMount of buttonComponent copy the props to the state of buttonComponent.
This way each button will have its own state, which manages shown.
Button component:
import react from 'react';
interface buttonComponentProps{
shown: boolean;
}
interface buttonComponentState{
shown: boolean;
}
class buttonComponent extends react.Component<buttonComponentProps,{}>{
constructor(props:buttonComponentProps){
super();
this.state{
shown:props.shown
}
}
....
}
export default buttonComponent;
I'm new to js and reactjs. I'm trying to create a ButtonGroup with few Buttons inside, in hope of when I click a particular Button (in ButtonGroup) only that particular button will get highlighted (change colour) and rest will be of normal colour.
Below is the code which does the above behaviour but in setColour method I'm getting an error _this.state.selected.props is undefined. Could someone point out the where I'm getting wrong ? Also, if someone can tell me if this is the correct way to approach this problem.
import React from "react"
import {
ButtonGroup,
Button
} from "reactstrap"
class MainButtonsGroup extends React.Component {
constructor (props) {
super(props)
this.state = {
selected: null
}
}
handleSelection = (e) => {
this.setState({selected: e.target})
}
setColour = (key) => {
if (this.state.selected)
{
// ERROR : _this.state.selected.props is undefined
return (this.state.selected.props.key === key) ? 'primary' : 'secondary'
}
}
render() {
return (
<ButtonGroup>
<Button key={1} onClick={this.handleSelection} color={this.setColour(1)}>MainButtonA</Button>
<Button key={2} onClick={this.handleSelection} color={this.setColour(2)}>MainButtonB</Button>
<Button key={3} onClick={this.handleSelection} color={this.setColour(3)}>MainButtonC</Button>
</ButtonGroup>
)
}
}
export default MainButtonsGroup;
You should not hold on to the e.target reference and you must be getting a React warning in your console about it? You just created a memory leak in your app.
Instead copy what you need from the target and let the reference be garbage collected. Although in your case there's no need to be attaching data to the DOM node:
<Button onClick={() => this.handleSelection(this.setColour(3))}>MainButtonC</Button>
Note you don't need key={3} unless you're mapping the elements in a loop.
handleSelection = (color) => {
this.setState({ selected: color })
}
However this code is a bit strange, just record the index of the clicked button and give it a class to style it e.g.
class MainButtonsGroup extends React.Component {
state = {
selectedIndex: null,
}
handleSelection = (index) => {
this.setState({selectedIndex: index})
}
render() {
const idx = this.state.selectedIndex;
return (
<ButtonGroup>
<Button className={idx === 1 ? 'primary' : 'secondary'} onClick={() => this.handleSelection(1)}>MainButtonA</Button>
<Button className={idx === 2 ? 'primary' : 'secondary'} onClick={() => this.handleSelection(2)}>MainButtonB</Button>
<Button className={idx === 3 ? 'primary' : 'secondary'} onClick={() => this.handleSelection(3)}>MainButtonC</Button>
</ButtonGroup>
);
}
}
You cannot get the props of a component from a DOM node. You could instead keep your button names in an array in your component state and use that to render your buttons in the render method.
You can then pass in the button name to the handleSelection and use that as your selected value. If your button is the selected one it can be given the primary color, otherwise the secondary one.
Example
import React from "react";
import ReactDOM from "react-dom";
import { ButtonGroup, Button } from "reactstrap";
import "bootstrap/dist/css/bootstrap.min.css";
class MainButtonsGroup extends React.Component {
constructor(props) {
super(props);
this.state = {
buttons: ["A", "B", "C"],
selected: null
};
}
handleSelection = button => {
this.setState({ selected: button });
};
render() {
const { buttons, selected } = this.state;
return (
<ButtonGroup>
{buttons.map(button => (
<Button
key={button}
onClick={() => this.handleSelection(button)}
color={selected === button ? "primary" : "secondary"}
>
MainButton{button}
</Button>
))}
</ButtonGroup>
);
}
}