setTimeout() outputs a non expected number/timer - reactjs

I'm trying to call the creator of action inside my Action.js to remove the alert after 3000ms with setTimeout(), and it outputs a number/timer 61 at the end of the message.
How can I remove this 61.
Output:
Password too short (min 6 characs.)61
Code:
const Alert = props => {
return (
<div>
{props.alert && (
<div className={`alert alert-${props.alert.type}`}>
<i className="fas fa-info-circle"> {props.alert.msg}</i>
{setTimeout(() => props.removeAlert(), 3000)}
</div>
)}
</div>
);
};
Thank you.

You should structure your code correctly like this:
const setAlert = props =>
{
if(props.alert){
setTimeout(() => props.removeAlert(), 3000)
return (props.alert.msg);
}
};
Don't put everything under the return statement if it is just action and is not generating any value for you
I strongly recommend you to read the book 'Clean Code' by Robert C. Martin

I hope this can help you:
Try to separate concerns in your component, in this case the render and the set time out:
What version of react are you using?
If you have higher that 16.8
Hooks run after each render, including the first one so: you can try something like this:
import React, { useEffect } from 'react';
useEffect(() => {
// Run your setTimeout here!!!
});
return (
// Render your component
);
Or if you are using a lower version of react you can convert your component in a class component and instead of calling the setTimeout in the render method, use it in the componentDidMount()
class Alert extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
setTimeout(() => this.props.removeAlert(), 3000)
}
render() {
return (
// Set your conditions &&
// Render your message or component
);
}
}

Related

Reactjs IF statement

I have a component I call that is a passed a recordID and returns the text associated to the Id. 33 should = Tower
will render "Tower" on the screen. All good, but...
When I try to use the component in the following IF statement it does not work.
...
if (<GetAssetTypeNameComponent datafromparent = {assettype_assettypeId}/> === "Tower")
{
this.props.history.push(`/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}`);
}
Using the passed parameter does work if I change the code to:
...
if (assettype_assettypeId === "33")
{
this.props.history.push(`/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}`);
}
...
What am I doing wrong?
Rob
Component Code that needs to be a Function....
...
class GetAssetTypeNameComponent extends Component {
constructor (props){
super(props)
this.state = {
assettype:[]
}
}
componentDidMount()
{
AssetTypeService.getAssetTypeById(this.props.datafromparent).then( (res) =>{
let assettype = res.data;
this.setState({isLoading:false});
this.setState({
assettypeName: assettype.assettypeName,
assettypeType: assettype.assettypeType
});
});
}
render() {
return (
<div>
{this.state.assettypeName}
</div>
);
}
}
export default GetAssetTypeNameComponent;
...
Following Function code compiles:
...
import React, { useState} from 'react';
import AssetTypeService from './AssetTypeService'
const GetAssetTypeNameFunction = (props) =>{
// destructuring
const { assettype_assettypeId } = props;
const [assetType,setAssetType] = useState()
AssetTypeService.getAssetTypeById(assettype_assettypeId).then( (res) =>
setAssetType(res.data));
const arrayMap = assetType.map((post)=>{
return(
<ul>
{post.assettypeName}
</ul>
);})
return (
{arrayMap}
);
}
export default GetAssetTypeNameFunction;
...
Get execution error:
I think because I calling the function from within an eventHandler:
...
editAssets(assetsid,assettype_assettypeId){ if (GetAssetTypeNameFunction(assettype_assettypeId) === "Tower") { this.props.history.push(/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}); }]
...
----- Error: Invalid hook call. Hooks can only be called inside of the body of a function component. I am responding to a onClick in a list to route to a specific component based on the function $
How do I get around this?
A component renders content to be displayed in the page. The retuned value of rendering a component is a tree of nodes that contain your content. All this means that <GetAssetTypeNameComponent> may contain the text content Tower, but it is not equal to the string "Tower". It just doesn't make any sense to render a component as the test for a conditional like this.
In React you want to use logic to tell react how to render. You do not want to render and then use the result in your logic.
It's hard to give advice on the best way to fix that with so little code, but maybe you want a a simple function to coverts the id into some text for you.
function getAssetName(id) {
return someLogicSomewhere(id).result.orWhatever
}
And now you can do something like:
if (getAssetName(assettype_assettypeId) === 'Tower')
{
this.props.history.push(
`/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}`
);
}

Trying to run function only if state changes

I want to run the set totals function only if the hour's state has changed. It is running every time the component mounts instead of only if the value changes. The this.state is apart of a context file that is extremely large so I only pasted the function being used
context.js (Class Component)
set Total
if (this.state.hours > 0) {
this.setState((prevState) => {
if (prevState.hours !== this.state.hours) {
console.log(prevState.hours);
}
return {
total: this.state.total + this.state.hours * ratePerHour * Math.PI,
};
});
console.log(this.state.total, '+', this.state.hours, '*', ratePerHour);
}
This is my component tha
import React, { useState, useEffect, useContext,useRef } from 'react';
import { ProductContext } from '../pages/oniContext';
import { Container,Badge } from 'reactstrap';
import {
Subtitle,
Description,
Titlespan2,
} from '../components/common/title/index';
import { total } from '../components/total';
export const FinalQuote = () => {
const pCR = useContext(ProductContext);
const prevCountRef = useRef();
useEffect(() => {
alert('Run')
console.log(pCR.hours, 'Final Quote Run', pCR.total);
pCR.setTotal();
console.error(pCR.hours);
}, [pCR.hours]);
return (
<section className="testimonial-wrapper gradient-color" id="testimonial">
<Container>
<div className="main-title-wrapper">
<Subtitle Class="site-subtitle gradient-color" Name="Your Quote" />
<Titlespan2
Class="sitemain-subtitle"
Name={`$${Math.round(pCR.total)}`}
/>
<Description
Class="site-dec"
Name="The Shown Price is only an estimate and may increase or decrease based on demand and extent of work"
/>
{pCR.activeAddOns.map((service, index) => (
<Badge color="info" pill>
{service.title}
</Badge>
))}
</div>
</Container>
</section>
);
};
You can achieve this by using componentDidUpdate life cycle function in your component class. As per the docs
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Means, whenever the state of the component will change, the componentDidUpdate code block will be called. So we can place an if condition in the block to compare the new state with the previous state, can calculate total and recommit it to the state. Code 👇
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
hours: 0,
total: 0,
ratePerHour: 10
};
}
componentDidUpdate(prevProps, prevState) {
if (prevState.hours !== this.state.hours) {
// Calculate total
this.setState({
total: this.state.total + this.state.hours * this.state.ratePerHour * Math.PI
}
}
}
render() {
return <AnotherComponent />;
}
}
Plus it is important to note that (ref: docs)
You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop.
In case of any other query, please feel free to reach out in the comments.
It's been a minute since I've worked with newer React features but when I can I use useEffect in my functional components. The second parameter is the the variable you want to watch for changes. If you don't supply a second parameter it'll run similar to componentDidMount and componentDidUpdate. An example of possible use:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
const [test, setTest] = useState('');
// Specify to watch count because we have more than one state variable
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Here's some of their documentation: https://reactjs.org/docs/hooks-effect.html
In my case, even when I added the second argument to useEffect which is an array of dependencies. It is running every time the component mounts instead of only if the value changes and I guess this is because I initialized my state variable like so
const[myStateVariable, setMyStateVariable] = React.useState('');
so I had to make this condition in my useEffect function
React.useEffect(() => {
if(myStateVariable !==''){
getMyData()
}
}, [myStateVariable]);

Render issue caused by setState executed every 0.1 sec in setInterval

I'm trying to click on a div that has the onClick function associated with it, but the function doesnt getting called due to the fact that I have a setState inside a setInterval called every 0.1 sec. This updates the DOM and doesnt let me call the function onClick.
I tried to use PureComponent and React.memo to avoid re-renders nested Components, but it didn't work; I could not use them properly though.
Inside the father constructor I have, basically, this:
setInterval(()=> {
this.setState({state1: 0})
}, 100)
}
EDIT
I'm proud of showing you the minimum (almost) code to test the issue (note the functional component: if you remove it and replace the < F /> with its content, it will work properly. Also, if you debug with google chrome, you will see what's going on the DOM):
class App extends Component {
constructor() {
super()
this.state = {state1: 0}
setInterval(() => {this.setState({state1: this.state.state1 + 1})}, 100)
}
render() {
const F = () => (
<button onClick={()=> alert("this function will be called... sometimes")}>
test: {this.state.state1}
</button>
)
return <div> <F/> </div>
}
}
EDIT 2
If I write the functional component as a pure function, it will work. here's the example:
class App extends Component {
constructor() {
super()
this.state = { state1: 0}
setInterval(() => {this.setState({state1: this.state.state1 + 1})}, 100)
}
render() {
const F = ({state1}) => (
<button onClick={()=> {alert("called sometimes")}}> test (will be called sometimes): {state1} </button>
)
function f(state1) {
return <button onClick={()=> {alert("called always")}}> test(will be called always): {state1} </button>
}
return <div> <F state1={this.state.state1}/> {f(this.state.state1)}</div>
}
}
setState will, by default rerender components that are impacted by said state.
This was answered here.
I would suggest moving away from setting state that often. That's quite expensive and I'm betting there is a far more efficient way to accomplish whatever it is that you're trying to do without the interval.
If you are using React 16.8^ then you could use Hooks to make multiple state changes. Note that instead of using setInterval i used setTimeout for each cycle
import React, {useState, useEffect} from 'react';
const App = (props) => {
const [numberShown, setNumberShown] = useState(0);
useEffect(() => {
setTimeout(() => setNumberShown(numberShown + 1), 100);
}, [numberShown]);
return (<div>
{numberShown}
</div>)
}
Hope it helps
EDIT : I found a way to do it the Component Way:
class App extends Component {
constructor() {
super()
this.state = {state1: 0}
this.startTimer = this.startTimer.bind(this)
this.startTimer()
}
startTimer = () => {
setInterval(() => {this.setState({state1: this.state.state1 + 1})}, 100)
}
render() {
return <button onClick={()=> alert("this function will be called... sometimes")}>
test: {this.state.state1}
</button>
}
}
By experimenting i noticed that if you render the button like this:
render() {
const F = () => (
<button onClick={()=> alert("this function will be called... sometimes")}>
test: {this.state.state1}
</button>
)
return <div> <F/> </div>
}
Instead of directly returning the button tag, the nested onClick function triggering the alert won't always go off but if you do it like in my example it will always trigger.

How to correctly initialize a function in React?

tell me, please, how to solve the following problem correctly?
I have a certain component, there is a control above, when I click on it, setState is triggered. I need to call the function this.setScrollLeft () in which I set to the selected node (ref) in this case the cleavage position.
Here is my implementation, but I am sure that there is a better solution:
import React from 'react';
import { ScoreCell, getScoreTheme } from 'components/scores';
class LeaderboardPlayerResult extends React.Component {
constructor(props) {
super(props);
this.containerWidth = 198;
this.data = this.props.data;
this.playerResultRef = React.createRef();
}
componentDidMount() {
this.element = this.playerResultRef.current;
this.element.scrollLeft = this.containerWidth;
}
setScrollLeft = () => {
if (this.element) {
this.element.scrollLeft = this.containerWidth;
}
};
playerResult = () => {
if (this.data.playOffHoles) {
return (
this.data.playOffHoles.map((item, index) => {
return (
<div
className="leaderboard__player-result-row-wrapper"
key={index}
>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holeId}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holePar}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell leaderboard__player-result-cell--score">
<ScoreCell
childCss='tee-times-card__score'
theme={getScoreTheme(item.playOffParScore)}
>{item.playOffParScore}</ScoreCell>
</div>
</div>
</div>
);
})
);
}
};
render() {
console.log('LeaderboardPlayerResult render');
this.setScrollLeft();
return (
<div
className="leaderboard__player-result"
ref={this.playerResultRef}
>
{this.playerResult()}
</div>
);
}
}
The best place to put this.setScrollLeft() is inside the componentDidUpdate method.
You are already calling this method (this.setScrollLeft()) inside componentDidMount, what is right. Now, you could put another call into componentDidUpdate and it will work pretty much as it is working by now because componentDidUpdate is called before render.
The final outcome will be the same, however, you are separating the concerns: render only render the components and the other methods deal with your business logic.
If you are not sure about componentDidMount and componentDidUpdate, see these excerpts from the official React.js documentation:
componentDidMount()
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
componentDidUpdate()
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.

How can I change what is rendred with a click event in React

I'm new to React, and I'm trying to figure out how to adjust what appears in render based on a click event. My component receives two props "front" and "back". I want the component to display this.props.front upon rendering and change to this.props.back when the div is clicked. I'm having trouble figuring out how to accomplish this in my handleClick function.
Any help would be appreciated!
import React, { Component } from 'react';
class Card extends Component {
handleClick = event => {
}
render() {
return (
<div className="Card" onClick={this.handleClick}>
<h1>{this.props.front}</h1>
</div>
);
}
}
export default Card;
You could add a state to this component which is a boolean that toggles itself
class Card extends Component {
constructor(props) {
this.state = {
showFront: true
}
}...
And than use your handleClick method to switch the state back and forth
handleClick = (e) => {
this.setState({showFront: !this.state.showFront})
}
And in your render function you could put a conditional to show
render() {
return (
<div className="Card" onClick={this.handleClick}>
{
this.state.showFront
? <h1>{this.props.front}</h1>
: <h1>{this.props.back}</h1>
}
</div>
);
}
A comment to this answer was made but was deleted - i think it's a subject worth touching.
the comment said you should use the setState(updater()) and not pass an object.
it's true that when the app becomes more complex, you have several state updates together and data states may not be what you believe they are at that moment, updater function is apropriate (setState is async and could batch calls this is why we have the function that flushes all and helps us maintain state integrity comparing old states with new ones.
but for this answer and the complexity of the question an updater isn't necessary and the code should work just fine (and it gets to the point of using state and toggling which is the right way of doing what was asked).
you can use the updater function any time you please - even for the most simplest state change. And like said here, maybe it is best practice to just always use it :)
for more reference
React.Compoment setState & Updater function
In react you trigger render by changing the state of component. If this component needs to recieve props "front" and "back" then parent component should have saved in state if the state is "front" or "back" and pass down to component callback function to handle change. Something like:
import React, { Component } from 'react';
class ParentCard extends Component {
state = { isFront: true };
handleClick = event => {
this.setState({isFront: !this.state.isFront})
}
render = () => {
const { front } = this.state;
return (
<Card front={front} onClick={this.handleClick} />
);
};
export default ParentCard;
Also you can make Card component "pure" just by creating it as function which returns JSX.
import React from 'react';
const Card = ( { isFront, onClick } ) => {
return (
<div className="Card" onClick={onClick}>
<h1>{isFront ? `text if is front` : `text if it is not`}</h1>
</div>
);
}
}
export default Card;
Hope it helps :)
I'd say in that case you want to use state rather than props here, particularly when the state you want to change is being dictated by the component itself.
class Card extends Component {
state = {
mode: 'front' // default state to front
}
handleClick = () => this.setState({ mode: 'back' })
render() {
return (
<div className="Card" onClick={this.handleClick}>
<h1>{this.props.mode}</h1>
</div>
);
}
}
export default Card;
If this is really a toggle then of course you can use a Boolean flag instead, but you get the idea.
This component itself is currently not set up as a stateless functional component so if thats what you also wanted to achieve. Youll want to make these changes as well as pass props of a boolean in your stateful component.
import React from 'react';
const Card = (props) => {
return (
<div className="Card" onClick={props.handleClick}>
{props.showFront
?
<h1>props.front</h1>
:
<h1>props.back</h1>
}
</div>
);
}
export default Card;
you'll want to utilize the previous state to toggle your state because it could cause issues later down the road with batching, so your stateful component should look something like:
import React, {Component} from "React";
class StatefulCard extends Component {
state = {
showFront: true // default state to front
}
handleClick = () => {
this.setState(prevState => {
return {
showFront: !prevState.showFront
}
}
}
render() {
return (
<div>
<Card
handleClick={this.handleClick}
showFront={this.state.showFront}
/>
</div>
);
}
}
export default Card;

Resources