Disable button when clicked in React JS - javascript

I am trying to disable a button on click in React JS, as its function is to add articles to an array. When as user clicks saves article, the button should disable, so they can't save again.
So far for this component my code is as follows:
import React, { Component } from 'react';
import './news-hero.css';
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
const responsive = {
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 1,
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 1,
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 1,
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1,
},
};
class NewsHero extends Component {
_isMounted = false;
state = {
loading: false,
data: [],
headline: [],
saved: []
}
saved = headline => {
this.setState(
(prevState) => ({ saved: [...prevState.saved, headline] }),
() => {
console.log('Saved articles = ', this.state.saved);
alert('Article saved');
localStorage.setItem('saved', JSON.stringify(this.state.saved));
localStorage.getItem('saved');
});
}
constructor(props) {
super(props)
this.saved = this.saved.bind(this)
}
onError() {
this.setState({
imageUrl: "../assets/img-error.jpg"
})
}
componentDidMount() {
this._isMounted = true;
this.setState({ loading: true, saved: localStorage.getItem('saved') ? JSON.parse(localStorage.getItem('saved')) : [] })
fetch('https://newsapi.org/v2/everything?q=timbaland&domains=rollingstone.com,billboard.com&excludeDomains=townsquare.media&apiKey=xxxx')
.then(headline => headline.json())
.then(headline => this.setState({ headline: headline.articles, loading: false }, () => console.log(headline.articles)))
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
return (
<div className="hero">
<h2 className="text-left">News</h2>
{this.state.loading
? "loading..."
: <div>
<Carousel
additionalTransfrom={0}
showDots={true}
arrows={true}
autoPlaySpeed={3000}
autoPlay={false}
centerMode={false}
className="carousel-hero"
containerClass="container-with-dots"
dotListClass="dots"
draggable
focusOnSelect={false}
infinite
itemClass="carousel-top"
keyBoardControl
minimumTouchDrag={80}
renderButtonGroupOutside={false}
renderDotsOutside
responsive={responsive}>
{this.state.headline.map((post, indx) => {
return (
<div className="text-left mt-5" key={indx}>
<img className="media-img card-img-top card-img-hero" src={post.urlToImage} alt="Alt text"></img>
<div className="card-body container hero-text-body">
<h1 className="card-title hero-title text-truncate">{post.title}</h1>
<button className="btn-primary btn mt-2 mb-4" onClick={() => this.saved(post)}>Save article</button>
<p className="card-text">{post.description}</p>
Read More
</div>
</div>
)
})}
</Carousel>
</div>
}
</div>
)
}
}
export default NewsHero;
Other questions answers don't really answer this straight question as the other questions are bespoke answers to other scenario codes.

You can pass the event to your onClick() function.
onClick={(e) => this.saved(e, post)}
You can then use that event and currentTarget to get the HTML element that contains the onClick and disable that element.
saved = (e, headline) => {
// Disable clicked button
e.currentTarget.disabled = true;
...
}

As Ed Lucas suggest, you can do it like that or, you can maintain your button status on the state and change it according to click event.
import React, { Component } from 'react';
import { render } from 'react-dom';
class App extends Component {
constructor() {
super();
this.state = {
btnStatus: false
};
}
render() {
return (
<div>
<button
disabled={this.state.btnStatus}
onClick={() => this.setState({ btnStatus: true })}
>
CLICK
</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));

Related

How the image is saved in the Draftwysiwyg in ReactJS

I use the Draft wysiwyg for my React project .
But I can only get simple text, but I can not get and save the image.
How can I save both text and image on my server using Draft wysiwyg?
component text editor:
class TextEditor extends Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
uploadedImages: [],
};
this._uploadImageCallBack = this._uploadImageCallBack.bind(this);
}
onEditorStateChange = (editorState) => {
this.setState({
editorState,
});
};
_uploadImageCallBack(file) {
let uploadedImages = this.state.uploadedImages;
const imageObject = {
file: file,
localSrc: URL.createObjectURL(file),
};
uploadedImages.push(imageObject);
this.setState({ uploadedImages: uploadedImages });
return new Promise((resolve, reject) => {
resolve({ data: { link: imageObject.localSrc } });
});
}
render() {
const { editorState } = this.state;
return (
<div style={{ height: "100%", width: "100%" }}>
<Editor
editorState={editorState}
toolbarClassName='toolbarClassName'
wrapperClassName='wrapperClassName'
editorClassName='editorClassName'
onEditorStateChange={this.onEditorStateChange}
toolbar={{
inline: { inDropdown: true },
list: { inDropdown: true },
textAlign: { inDropdown: true },
link: { inDropdown: true },
history: { inDropdown: true },
image: { uploadCallback: this._uploadImageCallBack },
inputAccept:
"application/pdf,text/plain,application/vnd.openxmlformatsofficedocument.wordprocessingml.document,application/msword,application/vnd.ms-excel",
}}
/>
</div>
);
}
}
export default TextEditor;
I use the TextEditor component, which is a class component, in another component, which is the functional component, and from there I send the data to the server.
thank for those to help me!

componentDidUpdate(prevProps, prevState, snapshot): prevProps is undefined

I am pretty new to React and I am trying to build this simple web app that takes a stock tag as an input and updates the graph based on the performance of the given stock. However, I can't get my graph to update. I tried using componentDidUpdate(prevProps, prevState, snapshot), but for some reason prevProps is undefined and I don't know/understand why. I tried searching online and reading the doc file, but I still can't figure it out. Any help would be appreciated.
import Search from './Search.js'
import Graph from './Graph.js'
import Sidebar from './Sidebar.js'
import './App.css'
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [{
x: [],
close: [],
decreasing: { line: { color: '#FF0000' } },
high: [],
increasing: { line: { color: '#7CFC00' } },
line: { color: 'rgba(31,119,180,1)' },
low: [],
open: [],
type: 'candlestick',
xaxis: 'x',
yaxis: 'y'
}]
,
layout: {
width: 1500,
height: 700,
font: { color: '#fff' },
title: { text: 'Stock', xanchor: "left", x: 0 }, paper_bgcolor: '#243b55', plot_bgcolor: '#243b55', yaxis: { showgrid: true, color: '#fff' },
xaxis: {
zeroline: true, color: '#fff', showgrid: true, rangeslider: {
visible: false
}
}
},
searchfield: '',
stocktag: ' '
};
this.onSearchChange = this.onSearchChange.bind(this);
this.onSubmitSearch = this.onSubmitSearch.bind(this);
}
componentDidMount() {
document.body.style.backgroundColor = '#243b55';
this.loadGraphInfo();
}
componentDidUpdate(prevProps, prevState, snapshot){
console.log(prevProps.stocktag);
console.log(prevState.stocktag);
if (prevProps.stocktag !== prevState.stocktag) {
//this.fetchData('SPY');
}
}
onSearchChange = (event) => {
var search = event.target.value;
this.setState({ stocktag: search });
}
onSubmitSearch = (e) => {
var search = this.state.searchfield;
this.setState({ stocktag: search });
}
fetchData(stock) {
//GET DATA
//UPDATE STATE
}
loadGraphInfo() {
if (this.state.stocktag == ' ') {
this.fetchData('SPY');
} else {
this.fetchData(this.state.stocktag);
}
}
render() {
return (
<div className="App" >
<Sidebar />
<Search searchChange={this.onSearchChange} submitChange={this.onSubmitSearch} />
<Graph data={this.state.data} layout={this.state.layout} />
</div>
);
}
}
export default App;
import React, { Component } from 'react';
import './Search.css'
const Search = ({ searchChange, submitChange }) => {
return (
<div>
<div class="SearchCompInput">
<input class="SearchBar" type="text" onChange={searchChange}/>
</div>
<div class="SearchCompButton">
<button class="SearchButton" onClick={submitChange}>Search</button>
</div>
</div>
);
}
export default Search;
The prevProps.stocktag is undefined because you didn't pass any props to App component. Try this in your index.js you will see preProps value but actually it does not make any sense.
render(<App stocktag='' />, document.getElementById('root'));
componentDidUpdate(prevProps, prevState, snapshot){
console.log(prevProps.stocktag);
console.log(prevState.stocktag);
if (prevProps.stocktag !== prevState.stocktag) {
//this.fetchData('SPY');
}
}
I am not quite sure on what you are trying to accomplish here but the first thing I notice is you setState of stocktag to this.state.searchfield which is ' ' in your onSubmitSearch function.
onSearchChange = (event) => {
var search = event.target.value;
this.setState({ stocktag: search });
}
onSubmitSearch = (e) => {
var search = this.state.searchfield;
this.setState({ stocktag: search });
}
Add I will also like to add that it is good practice to set value of input to a state value like so
import React, { Component, useState } from 'react';
import './Search.css'
const Search = ({ searchChange, submitChange }) => {
const [inputValue, setInputValue] = useState('')
const handleChange = (e) => {
setInputValue(e.target.value)
searchChange(e)
}
return (
<div>
<div class="SearchCompInput">
<input class="SearchBar" type="text" value = {inputValue} onChange={handleChange}/>
</div>
<div class="SearchCompButton">
<button class="SearchButton" onClick={submitChange}>Search</button>
</div>
</div>
);
}
export default Search;
I had this problem, and it was because there was a child class that was calling super.componentDidUpdate() WITHOUT passing in the parameters. So the child class looked something like:
componentDidUpdate() {
super.componentDidUpdate();
... <-- other stuff
}
And I had to change it to:
componentDidUpdate(prevProps, prevState) {
super.componentDidUpdate(prevProps, prevState);
... <-- other stuff
}

React - Show loader on Click that already has function assigned to it

I have already a a click event within a ternary operator that does a GET request from my API. When the button is clicked, the button disappears and the data text replaces the button (button disappears). But there is a small gap of time between the get request and the text reveal. I want to put a loading message of some kind at that moment of time so the user knows something is happening. But can't seem to figure it out. Here is my code:
import React, {Component} from "react";
import axios from "axios";
export default class FoodData extends React.Component {
constructor(props) {
super(props);
this.state = {
meal: '',
clicked: false,
isLoaded: false,
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
clicked: true,
});
}
fetchData() {
axios.get('api/menu/food')
.then(res => {
const meal= `${res.data.starters},${ res.data.price}`;
this.setState({
meal: meal,
isLoaded: true
})
console.log(meal)
})
};
combinedFunction() {
this.fetchData();
this.handleClick();
}
render(){
const {isLoaded, meal} = this.state;
return (
<div >
Dish: {
this.state.clicked ? (
this.state.menu
) : (
<button onClick={() => { this.combinedFunction() }}>Find Dish</button>
)}
</div>
);
}
}
Appreciate the help.
What you can do is add a "isLoading" state and put the values before and after your API call like so:
fetchData() {
this.setState({isLoading: true});
axios.get('api/menu/food')
.then(res => {
const meal= `${res.data.starters},${ res.data.price}`;
this.setState({
meal: meal,
isLoaded: true
isLoading: false,
})
console.log(meal)
})
};
And use that on your render to show the "loading icon"
render(){
const {isLoaded, meal, isLoading } = this.state;
return (
<div >
{isLoading ? <div>loading</div> :
Dish: {
this.state.clicked ? (
this.state.menu
) : (
<button onClick={() => { this.combinedFunction() }}>Find Dish</button>
)}
}
</div>
);
}
}
This is a working demo which shows loading when api call starts and disables button to prevent multiple api calls. I added a 2sec time out to show the demo. Check the stackblitz sample
This is the updated code, here I used a fake api (https://jsonplaceholder.typicode.com/users) to show the demo
import React, {Component} from "react";
import axios from "axios";
export default class FoodData extends React.Component {
constructor(props) {
super(props);
this.state = {
meal: '',
clicked: false,
isLoaded: false,
}
this.handleClick = this.handleClick.bind(this);
this.combinedFunction = this.combinedFunction.bind(this)
}
handleClick() {
this.setState({
clicked: true,
});
}
fetchData() {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(res => {
this.setState({
meal: res.data,
isLoaded: false
})
})
};
combinedFunction =()=> {
this.setState({isLoaded: true})
setTimeout(()=>{
this.fetchData();
},2000)
this.handleClick();
}
render(){
const {isLoaded, meal} = this.state;
return (
<>
<div >
Users:
<button onClick={this.combinedFunction } disabled={isLoaded ? true : false}>{isLoaded ? 'Loading...':'Find User'}</button>
</div>
<div>
{meal && meal.map(item =>(
<div key={item.id}>
<p>{item.id} - {item.name}</p>
</div>
))}
</div>
</>
);
}
}

How to automatically change an img depending on boolean value in JSON file

I have a JSON file:
[
{
"id": 1,
"availability": false
},
{
"id": 2,
"availability": true
}
]
What I would like to achieve is to automatically display an image of a tick if availability : true and to display an image of a cross if availability : false.
For example these are the names of the two images:
tick.jpg
cross.jpg
This is my code so far:
import React, { Component } from "react";
import "./styles.css";
class GetOnlinePosts extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
posts: []
};
}
componentDidMount() {
fetch("https://api.myjson.com")
.then(response => response.json())
.then(
result => {
this.setState({
isLoaded: true,
posts: result
});
},
error => {
this.setState({
isLoaded: true,
error
});
}
);
}
render() {
const { error, isLoaded, posts } = this.state;
const orderedPosts = [
...posts.filter(post => post.availability),
...posts.filter(post => !post.availability)
];
if (error) {
return <div>Error in loading</div>;
} else if (!isLoaded) {
return <div>Loading ...</div>;
} else {
return (
<div>
<div className="tiles">
{orderedPosts.map(post => (
<div key={post.id}>
<div className="tile"></div>
</div>
))}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
Unfortunately I am unable to have the images included in the with JSON. I would like the images to be within the <div className="tile"> </div> so any help on how to do this would be great. Thanks in advance.
<img src={post.availability ? 'tick.jpg' : 'cross.jpg'} />

Memory leaks when I do log out

I'm getting memory leaks when I'm using this library.
I would like know how can remove them when I did log out.
I guess that I must access inside this.notificationDOMRef and remove them but I dont know.
This is the library:REACT-NOTIFICATIONS
And in componentWillUnmount I do this:
componentWillUnmount() {
this.notificationDOMRef.current = undefined;
this.notificationDOMRef = undefined;
};
And where I create component I do this:
{ this.state.showMapList ? <MapList /> : null }
EDIT
import React from "react";
import ReactNotification from "react-notifications-component";
class Notifiaction extends Component {
constructor(props) {
super(props);
this.notificationDOMRef = React.createRef();
}
addNotification = () => {
this.notificationDOMRef.current.addNotification({
title: "Awesomeness",
message: "Awesome Notifications!",
type: "success",
insert: "top",
container: "top-right",
animationIn: ["animated", "fadeIn"],
animationOut: ["animated", "fadeOut"],
dismiss: { duration: 2000 },
dismissable: { click: true }
});
}
render() {
return (
<div className="app-content">
<ReactNotification ref={this.notificationDOMRef} />
<button onClick={this.addNotification} className="btn btn-primary">
Add Awesome Notification
</button>
</div>
);
}
}
export default Notifiaction;

Categories

Resources