Loading indicator when button is clicked (reactjs and ant design) - reactjs

I made a button that changes its label from 'extract' to 'extracted' when clicked then it becomes disabled. What I want to do now is for it to have a loading indicator when clicked and stop when the button is disabled.
I tried
document.getElementById(inputID).setAttribute("loading", "true");
when button is clicked but found out that it doesn't work on buttons. I am also trying right now to use setState with my
document.getElementById("btnTesting").innerHTML = "EXTRACTED";
class DashboardPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
}
onClickBtn = () => {
this.setState({ loading: true});
document.getElementById("btnTesting").innerHTML = "EXTRACTED";
document.getElementById("btnTesting").setAttribute("disabled","true");
}
render() {
return (
<Button id="btnTesting" onClick={this.onClickBtn} loading={this.state.loading}>EXTRACT</Button>
)
}
}
I expect to have a loading indicator when clicked then stop when button is disabled. But the screen turns blank. I look in the console and saw this error
Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
The above error occurred in the component:
blah .
blah .
Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.

Because you're using React, you don't need to use the innerHTML or setAttribute to change button text or disabled state.
You can use state varibles buttonText, initialised to "Extract" and isDisabled, initialised to false, and you can change the state after the button is done executing.
class DashboardPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
buttonText: "EXTRACT",
isDisabled: false
};
}
onClickBtn = () => {
this.setState({ loading: true });
setTimeout(() => {
this.setState({
buttonText: "EXTRACTED",
isDisabled: true,
loading: false
});
}, 2000);
};
render() {
return (
<Button
id="btnTesting"
onClick={this.onClickBtn}
loading={this.state.loading}
disabled={this.state.isDisabled}
>
{this.state.buttonText}
</Button>
);
}
}
I've added a setTimeout of 2000ms so you can see the loading indicator.
Find the demo here: https://codesandbox.io/s/antd-reproduction-template-l1d4r

Can you check if below code solves your issue ? I Added timer to demonstrate time affect so that loading becomes evident.
With ReactJS, you don't have to use setAttribute or innerHTML as ReactJS uses virtualDOM. Even if you use them, It can get DOM in inconsistent state. As per standard React practice you should handle your requirements (i.e changing DOM) with state variables
class DashboardPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: {"btn1": false},
disabled: false,
buttonText: 'Extract'
}
onClickBtn = (btnStr) => {
let loadingCopy = Object.assign({},this.state.loading)
loadingCopy[btnStr] = true
this.setState({loading: loadingCopy})
setInterval(() => {
loadingCopy[btnStr] = false
this.setState({ loading: loadingCopy, buttonText: 'Extracted', disabled: true });
}, 3000);
}
render() {
return (
<ButtononClick={() =>this.onClickBtn("btn1")} loading={this.state.loading["btn1"]} disabled={this.state.disabled}>{this.state.buttonText}</Button>
)
}
}

import { Button } from 'antd';
import React from "react";
class SubmitButton extends React.Component {
state = {
loadings: [],
};
enterLoading = index => {
this.setState(({loadings}) => {
const newLoadings = [...loadings];
newLoadings[index] = true;
return {
loadings: newLoadings,
};
});
};
render() {
const {loadings} = this.state;
return (
<>
<Button type="primary" htmlType="submit" loading={loadings[0]} onClick={() => this.enterLoading(0)}>
Submit
</Button>
</>
);
}
Code:
https://codesandbox.io/s/withered-framework-w5lmc?file=/src/App.js

Related

Why is setState(this.state) not triggering a rerender?

I am trying to get my component to rerender after deleting an entry from the table.
I have tried binding my functions and using this.forceUpdate() as well but nothing seems to work. Any help will be much appreciated!
This is my component
class Directory extends Component {
constructor() {
super();
this.state = {
tenantData: [],
searchText: "",
searchedColumn: "",
tenantInfo: {},
visible: false,
userId: null,
rerender: "",
};
// this.delTenant = this.delTenant.bind(this);
this.deleteAudit = deleteAudit.bind(this);
// this.deleteTenant = this.deleteTenant.bind(this);
// this.onDeleteClick = this.onDeleteClick.bind(this);
}
This is my delete function within my component
onDeleteClick = () => {
this.setState({
...this.state,
visible: false,
});
var tenantList = this.state.tenantData;
for (var i = 0; i < tenantList.length; i++) {
if (tenantList[i].userId == this.state.userId) {
console.log(this.state.userId);
delTenant({ _id: tenantList[i]._id });
deleteTenant({ _id: this.state.userId });
this.deleteAudit({ tenantID: tenantList[i]._id }).then(() => {
this.setState(this.state); // this should rerender the component but it does not
console.log("force update");
});
break;
}
}
And this is my AntD component
<Modal
title="Modal"
visible={this.state.visible}
onOk={this.onDeleteClick}
onCancel={this.hideModal}
okText="Confirm"
cancelText="Cancel"
>
<p>Are you sure you want to delete this Tenant?</p>
</Modal>
EDIT:
Soultion found - My component did rerender, but my tenantData state was unchanged as I forgot to get the data from the database after deleting the entry.
this.deleteAudit({ tenantID: tenantList[i]._id }).then(() => {
// this.setState(this.state); // this should rerender the component but it does not
this.getTenantFunction(); // this gets the new data from database and sets the state of tenantData to the updated one
});
this.setState(this.state); // this should rerender the component but it does not
React doesn't re-render your component if your state or props doesn't change.
I don't know why forceUpdate doesn't work, look at this example
import React from "react";
export default class App extends React.Component {
constructor() {
super();
this.state = {
tenantData: [],
searchText: "",
searchedColumn: "",
tenantInfo: {},
visible: false,
userId: null,
rerender: ""
};
}
onDeleteClick = () => {
console.log("click");
};
onDeleteClickForced = () => {
console.log("clickForced");
this.forceUpdate();
};
render = () => {
console.log("render");
return (
<>
<button onClick={() => this.onDeleteClick()}>Not forced</button>
<button onClick={() => this.onDeleteClickForced()}>Forced</button>
</>
);
};
}

React states remain undefined

I'm trying to set the in property of a bootstrap <Collapse> tag to true on a button click. But when I try to reference my is_open state its undefined.
class Graph extends Component {
constructor(props) {
super (props);
this.state = ({
is_open: false,
});
}
click_open = () => {
console.log(this.is_open); // logs undefined
this.setState({ is_open: !this.is_open });
}
render() {
return (
<div className='container>
<button onClick={this.click_open}>TAB</Button>
<Collapse in={this.is_open}></Collapse>
</div>
)
})
}
No matter what I do my state stays undefined. What am I missing here?
You're missing state
Change your code to be:
click_open = () => {
console.log(this.state.is_open);
this.setState({ is_open: !this.is_open });
}

Trigger a click event on element and then unmount it

I'm trying to implement a suggestion list. I use <input/> followed by a number of <div>s. When the input element is focused, list, or divs, will show; when the input element is not focused, or blurred, the divs are unmounted. I want to print the content in div before the boolean variable isFocused is set to false. But now if I blur the input and click div, the printContent will not be triggered because isFocused has been set to false. Now I am using setTimeout to deal with this problem. Is there a better way to resolve this? Here is the code snippet:
CodeSandbox
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isFocused: false
}
}
handleFocus = () => {
this.setState({ isFocused: true });
}
handleBlur = () => {
setTimeout(() => this.setState({ isFocused: false }), 1000);
}
printContent = () => {
console.log('print content');
}
render() {
console.log(this.state.isFocused)
return(
<div>
<input onFocus={this.handleFocus} onBlur={this.handleBlur} />
{
this.state.isFocused ?
<div key='1' onClick={this.printContent}>content1</div> :
null
}
</div>
);
}
}

What is the difference between this.varable and this.state.variable in React

What is the difference between
class App extends Component {
open = false
render () {
<Button onPress=(() => { this.open = false}) />
}
}
class App extends Component {
constructor() {
this.state({
open: false
})
}
render () {
<Button onPress=(() => { this.setState({ open: true })}) />
}
}
For me, it works the same, and I used this without a state and never had issues.
this.open is referring to the property of class App while this.state, if you are using React, it is referring to your components local state.
this.setState({ open: true }) will update the state and also rerenders your components while this.open = false will not trigger any rerenders
This is because you are not using open inside your render method. Changing state re-renders the component with different data, while class properties doesn't.
To better understand the difference, you could try clicking the button in these two classes:
class App extends Component {
open = false
render () {
let isOpen = "Not open"
if (this.open) {
isOpen = "Open!"
}
return (<View><Button onPress=(() => { this.open = false}) /><Text>{isOpen}</Text></View>)
}
}
class App extends Component {
constructor() {
this.state({
open: false
})
}
render () {
let isOpen = "Not open"
if (this.state.open) {
isOpen = "Open!"
}
return (<View><Button onPress=(() => { this.setState({ open: true })}) /><Text>{isOpen}</Text></View>)
}
}

Throttle hide/show component ReactJs

I'm new on react and now I have a problem. I have in my app 3 component:
App Component:
class App extends Component {
constructor(){
super();
this.state = {
showToast:false,
toastMessage: ""
};
this.handleShowToast = this.handleShowToast.bind(this);
};
handleShowToast(message) {
this.setState({ showToast: true, toastMessage: message});
setTimeout(function() {
if(this.state.showToast){
this.setState({ showToast: false });
}}.bind(this), 2000);
};
render() {
return (
<div id="cont">
<Toast showToast={this.state.showToast} messageToast={this.state.toastMessage} />
<Header handleToast={this.handleShowToast}/>
</div>
);
}
}
In my Header components I click on button and hadleShowToast is execute and Toast Component is correctly show and it work, but I would (now i make with setInterval, but doesn't work, because if I click a second time button it immediately hide because there is a precendent setInterval) hide Toast component, after 2 second, if I don't click again a button, but if I click again button in Header component i would reset timer.
How Can I do this?
You can use clearTimeout to clear timers:
handleShowToast(message) {
this.setState({ showToast: true, toastMessage: message});
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(function() {
if(this.state.showToast){
this.setState({ showToast: false });
}
this.timer = null;
}.bind(this), 2000);
}

Resources