React, Uncaught RangeError: Maximum call stack size exceeded - javascript

I'm working in react and basically I want to make an button with tooltip, right now I'm making tooltip. I'm changing css display property in order to make it visible or not during mouse enter and leave. But there is an error and I don't know what to do...
Here is my code:
import React from 'react';
import ReactDOM from 'react-dom';
import Style from 'style-it';
var Ink = require('react-ink');
import FontIcon from '../FontIcon/FontIcon';
var IconButton = React.createClass({
getInitialState() {
return {
iconStyle: "",
style: "",
cursorPos: {},
};
},
extend(obj, src) {
Object.keys(src).forEach(function(key) { obj[key] = src[key]; });
return obj;
},
Tooltip(props) {
var style = {};
if (this.tooltipDisplay) {
style.display = "block";
} else if (!this.tooltipDisplay) {
style.display = "none";
};
return <div className="tooltip" style={style}>{_props.tooltip}</div>;
},
showTooltip(){
this.tooltipDisplay = true;
},
removeTooltip(){
this.tooltipDisplay = false;
},
render() {
var _props = this.props,
tooltip = this.Tooltip,
opts,
tooltipDisplay = false,
disabled = false,
rippleOpacity,
outterStyleMy = {
border: "none",
outline: "none",
padding: "8px 10px",
"background-color": "red",
"border-radius": 100 + "%",
cursor: "pointer",
},
iconStyleMy = {
"font-size": 12 + "px",
"text-decoration": "none",
"text-align": "center",
display: 'flex',
'justify-content': 'center',
'align-items': 'center',
},
rippleStyle = {
color: "rgba(0,0,0,0.5)",
};
if (_props.disabled || _props.disableTouchRipple) {
rippleStyle.opacity = 0;
};
this.setState({
iconStyle: _props.iconStyle
});
this.setState({
style: _props.style
});
if (_props.disabled) {
disabled = true;
};
if (this.state.labelStyle) {
iconStyleMy = this.state.iconStyle;
};
if (this.state.style) {
outterStyleMy = this.state.style;
};
if (_props.href) {
opts.href = _props.href;
};
var buttonStyle = this.extend(outterStyleMy, iconStyleMy);
return(
<Style>
{`
.IconButton{
position: relative;
}
.IconButton:disabled{
color: ${_props.disabledColor};
}
.btnhref{
text-decoration: none;
}
`}
<a {...opts} className="btnhref" >
<tooltip text={this.props.tooltip} position={this.options} />
<button ref="button" className={"IconButton" + _props.className} disabled={disabled} style={buttonStyle}
onMouseEnter={this.showTooltip} onMouseLeave={this.removeTooltip} >
<Ink background={true} style={rippleStyle} opacity={rippleOpacity} />
<FontIcon className={_props.iconClassName}/>
</button>
</a>
</Style>
);
}
});
ReactDOM.render(
<IconButton href="" className="" iconStyle="" style="" iconClassName="face" disabled="" disableTouchRipple="" tooltip="aaaaa" />,
document.getElementById('app')
);
In console I'm getting this error:
Uncaught RangeError: Maximum call stack size exceeded
at defineRefPropWarningGetter (App.js:1053)
at Object.ReactElement.createElement (App.js:1220)
at Object.createElement (App.js:3329)
at Constructor.render (App.js:43403)
at App.js:15952
at measureLifeCyclePerf (App.js:15233)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (App.js:15951)
at ReactCompositeComponentWrapper._renderValidatedComponent (App.js:15978)
at ReactCompositeComponentWrapper._updateRenderedComponent (App.js:15902)
at ReactCompositeComponentWrapper._performComponentUpdate (App.js:15880)
I can't find out what's wrong. I know it might be something about calling a function which in turn calls another function. But I can't see anything like this in my code and I'm not sure if it's all about it. Thanks for help :)

The problem is you are calling setState from inside your render function. State changes should only happen as a result of something changing: user clicked on a button, the browser window was resized, a picture was taken, etc.
Never ever ever ever update the state while rendering (repeat that last sentence 20 times and never forget it).
Here is the problem code:
render () {
...
this.setState({
iconStyle: _props.iconStyle
});
this.setState({
style: _props.style
});
...
}
The above code would cause an infinite loop of sorts because setState causes render to be called. Since iconStyle and style are props, and props cannot change, you should use those props to build your initial state.
getInitialState() {
return {
iconStyle: this.props.iconStyle,
style: this.props.style,
cursorPos: {},
};
}
Later, if someone clicks a button and you want the iconStyle to change, you would create a click handler which updates your state:
handleClick () {
this.setState({
iconStyle: 'clicked'
});
}
This would cause your component to be rerendered and the new state would be reflected.
Think of your "state" as someone cooking and we are going to take photographs of them cooking. The initial state is "eggs cracked: no, flour poured: no, veggies chopped: no", and you take a picture of this state. Then the chef does something - cracks the eggs. The state has now changed, and you take a picture of it. Then she cuts the veggies. Again, the state has changed and you take a picture.
Each photograph in the analogy represents your "render" function - a snapshot of the "state" at a particular point in time. If every time you took a photograph the flour got poured, well we would have to take another picture because the flour was just poured. Taking another picture would cause more flour to get poured so we'd have to take another picture. Eventually you'd fill the kitchen to the ceiling with a celiac's nightmare and suffocate everybody in the room. You'd also run out of film or hard disk space on your camera.

Thanks to #RyanWheale I noticed my mistake.
In my render function I was returning a button element which called a function which changed a certain state. The returned button looked like this:
<button onclick={this.edit()} className="button-primary">Edit</button>
And my edit function changes some state and looks like this:
edit: function () {
this.setState({editing: true});
}
So, I my mistake is that I, accidentally, typed the parenthesis after this.edit. So, when the button element was being rendered, the edit function was actually called and chaos happened. Now, when I wrote
<button onclick={this.edit} className="button-primary">Edit</button>
instead of
<button onclick={this.edit()} className="button-primary">Edit</button>
it worked flawlessly.
I hope I help someone save hours of his precious life.
Cheers :)

I faced the same problem, I had installed "reactime" extension and that extension caused me this problem. Removing that extension from my chrome, solved the issue.

I got 'Maximum call stack size exceeded', and similar errors because of framer-motion API dependency, version bigger than 4.1.17 (today's version is 5.5.5). I don't figured out why yet.
For the same extensions also, got some weird errors like 'window.webkitStorageInfo' is deprecated, and similar bugs.

I had the same error in my SPFX project while running Gulp Serve. I deleted the newly added reference in my config.json file and it worked.
More details: https://fixingsharepoint.blogspot.com/2022/04/rangeerror-maximum-call-stack-size.html

Related

How to create an array of objects in React Native from seperate components?

I'm new to React Native, so I understand I have alot to learn.
I'm creating a custom class component here:
import React, { Component, useState } from 'react';
import {View,Text,StyleSheet,TextInput, Button} from 'react-native';
class Square extends React.Component{
constructor(pos,text,won,save){
this.state = {
pos : 0,
text : 'EDIT',
won : false,
save : false,
};
}
setPos = (pos) =>{
this.setState(pos)
}
getPos = () => {
return (this.pos);
}
setText=(text)=>{
this.setState(text)
}
getText=()=>{
return (this.text);
}
setWon=(won)=>{
this.setState(won)
}
getWon=()=>{
return (this.won);
}
setSave=(save)=>{
this.setState(save)
}
getSave=()=>{
return (this.save);
}
};
export default Square;
Then I want to create an array of those objects in a different component and display a piece of information from each object.
import {View, Text, StyleSheet, Button, Alert} from 'react-native';
import Square from '../components/Square';
const NewGameScreen = () => {
let arrSquare = [];
for (let i = 0; i < 25; i++){
arrSquare.push({
THIS IS WHERE I'M HAVING TROUBLE
});
}
console.log(arrSquare[0].getPos)
return(
<View style = {styles.screen}>
<View style = {styles.row}>
<Text>{arrSquare[0].getPos}</Text>
</View>
</View>
)
};
However from the above code I'm sure it's clear I'm missing something. I would have expected to use something like Square[i].setPos(i); but that throws errors. The console log also gives 'undefined' so that makes me think I haven't declared something or haven't declared it properly. Thanks in advance.
well the way I would go about this is to have a simple array of json object something like this:
let squareArr = [{
id: 0,
pos : 0,
text : 'EDIT',
won : false,
save : false,
},
{
id: 1,
pos : 0,
text : 'EDIT',
won : false,
save : false,
},
{
id: 2,
pos : 0,
text : 'EDIT',
won : false,
save : false,
}
]
then you can do the the read and the edit.
to display a position:
in your render method you can do this:
<View style = {styles.screen}>
<View style = {styles.row}>
squareArr.map((square) => <Text>{square.pos}</Text>)
</View>
</View>
to edit a position:
if you want to change a value in your JSON then just use the object index as a way to indicate which object you wanna change. For example want to change the pos of the second object then I would do this:
squareArr[1].pos = 3
I am not quite sure what is the whole project is to give you to give you the best solution but i hope this helps..
feel free to ask if you have any questions

How to render an Image by URL from Axios.get()

I have problems and need help with rendering an image in React. The problem is that this image is stored locally, and the URL to this image is retrieved from database with Axios.get() along with some other data. All data is showing up fine, but cannot render that image.
class Lesson extends Component {
state = {
lesson: null
};
componentDidMount() {
Axios.get("https://localhost:44372/api/Lesson/lesson/" +
this.props.location.pathname.substring(
this.props.location.pathname.lastIndexOf("/") + 1)
).then(response => {
console.log("API Response", response);
const newLesson = response.data;
this.setState({
lesson: newLesson,
});
});
}
render() {
return (<div>
<h2 style={{ textAlign: "center" }}>{this.state.lesson.title}</h2>
<p>{this.state.lesson.text}</p>
<p>{this.state.lesson.field1}</p>
<img src={this.state.lesson.img} //<-----here
alt="none"
style={{
verticalAlign: "middle",
display: "block",
margin: "auto",
marginLeft: "auto",
marginRight: "auto"
}}
/>
<div>);
In this way that image does not show up.
Error that I encounter is: Not allowed to load local resource: file:///D:/Project/FrontEnd/platform/src/images/python_img.PNG
The only way that I can get that img is to insert like this manually, but this is not what I want:
<img src={require("D:\\Project\\FrontEnd\\platform\\src\\images\\python_img.PNG")}
I thought that the problem is in componentDidMount(), as I was getting an undefined value at start, but later I just provided the same path from parent page (just to check) and the problem is the same.
I am doing something wrong...
Is a way to provide as image source a variable?
If the images itself is bundled in your react app, then its not going to work like you expect.
require() only accepts static
Things you can do
1.Render conditionally
const images = {
image1: require('./image1.jpg'),
image2: require('./image2.jpg'),
};
Then in your app
<img src={this.state.lesson.img === 'something_unique' ? images.image1 : images.image2} //<-----here
alt="none"
style={{
verticalAlign: "middle",
display: "block",
margin: "auto",
marginLeft: "auto",
marginRight: "auto"
}}
/>
2.Or if your using a bundler, you can get the public URL and append it.
<img src={process.env.PUBLIC_URL + this.state.lesson.img} />

How to pass one state value to another state on initialization in react js

I am trying to pass one state value that is imagesArray to another state that is tabData, but it is coming as undefined, PFB the code, please tell what i am doing wrong here?
constructor(props) {
super(props);
this.state = {
imagesArray: [
{
default: '/images/volImage1.png',
active: 'images/volImage1.png'
},
{
default: '/images/volImage2.png',
active: 'images/volImage2-Active.png'
},
{
default: '/images/volImage3.png',
active: 'images/volImage3.png'
},
{
default: '/images/volImage4.png',
active: 'images/volImage4.png'
},
{
default: '/images/volImage5678.png',
active: 'images/volImage5678.png'
},
],
tabData: [
{
title: 'Knowledge and experience',
content: <VolunteerTabContent1 imagesArray={this.state.imagesArray} />
//Here I am passing above imagesArray state, and this is coming as undefined and throwing error
},
{
title: 'Practical and hands on',
content: 'Tab 2 Content'
},
{
title: 'Management and leadership',
content: 'Tab 3 Content'
},
]
}
}
You cannot use this.state when setting the state itself. This won't work at all.
In your case, if imagesArray is not going to be changed during the execution and it's only some data you need, maybe you don't need to set it as part of the component's state.
You could define imagesArray as a constant outside the class or something similar, and just reference it when setting the tabData.
EDIT:
Even more. If tabData is just data you will need afterwards but it's not going to change, you don't need to set that as state either.
EDIT 2:
If this two arrays really need to be in the state, a way to achieve the desired results would be to define only the component name in the 'content' property of each tabData item, and then use that to instantiate it in the render method:
tabData: [
{
title: 'Knowledge and experience',
content: VolunteerTabContent1
},
...
and then in the render method you can do:
// Let's suppose you want the first one as an example. Do this as you need.
const item = this.state.tabData[0];
render() {
<item.content imagesArray={this.state.imagesArray} />
}
This way you'll correctly get the imagesArray form the state. JSX will get item.content's value (the VolunteerTabContent1 component in this case) and render it.
I will show in functional component it will useful for someone.
import "./styles.css";
import image1 from '../image/man.png';
import image2 from '../image/woman.png';
import React,{useState} from 'react';`enter code here`
import Avatar from '#mui/material/Avatar';
export default function App() {
const [choose,setChoose] =useState("")
const [avatar,setAvatar] = useState(image);
return (
<div className="App">
<Avatar
alt="D S"
src={avatar}
sx={{ width: 44, height: 44 }}
/>
<div>
<Avatar className='Avatars' onClick={()=> setChoose(image1)} alt="Guest"
src={image1} sx={{ width: 30, height: 30 }}/>
<label>Guest</label>
</div>
<div>
<Avatar className='Avatars' onClick={()=> setChoose(image2)} alt="Other"
src={image2} sx={{ width: 30, height: 30 }}/>
<label>Other</label>
</div>
<div>
<button className="avatar_btn1" onClick={()=>setAvatar(choose)} >OK</button>
</div>
</div>
);
}
from my code first you can choose a avatar it will not change in frontend when you click ok button it will show avatar changed one.

Rendered HTML not in sync with React component

I'm currently creating a custom React component in Meteor for adding images to a list (and later uploading them). However when I try to delete images from the list, always the last element is removed from the GUI. Initially I thought this was just a simple case of using the wrong index for deletion, but it turned out to be more than that.
This is what my ImageList component currently looks like:
import React from 'react';
import Dropzone from 'react-dropzone';
import cloneDeep from 'lodash.clonedeep';
import { ImageItem } from './image-item.js';
export class ImagesList extends React.Component {
constructor(props) {
super(props);
this.values = this.props.images || [];
this.onDrop = this.onDrop.bind(this);
this.addImages = this.addImages.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.imageChanged = this.imageChanged.bind(this);
}
onDrop(files) {
this.addImages(files);
}
onDropRejected() {
alert('Invalid file type');
}
addImages(files) {
files.forEach(file => {
this.values.push({
title: '',
description: '',
url: file.preview,
file,
});
});
this.forceUpdate();
}
deleteImage(index) {
console.log('index to delete', index);
console.log('images pre-delete', cloneDeep(this.values)); // deep-copy because logging is async
this.values.splice(index, 1);
console.log('images post-delete', cloneDeep(this.values)); // deep-copy because logging is async
this.forceUpdate();
}
imageChanged(index, image) {
this.values[index] = image;
this.forceUpdate();
}
render() {
console.log('--------RENDER--------');
return (
<div className="image-list">
<div className="list-group">
{this.values.length === 0 ?
<div className="list-group-item">
No images
</div>
:
this.values.map((image, index) => {
console.log('rendering image', image);
return (
<ImageItem
key={index}
image={image}
onDelete={() => { this.deleteImage(index); }}
onChange={(item) => { this.imageChanged(index, item); }}
deletable={true}
/>
);
})
}
</div>
<Dropzone
multiple={true}
onDrop={this.onDrop}
onDropRejected={this.onDropRejected}
className="dropzone"
activeClassName="dropzone-accept"
rejectStyle={this.rejectStyle}
accept={'image/*'}
>
<span>Drop files here</span>
</Dropzone>
</div>
);
}
}
The ImagesList component can be initialized with some values (for the sake of debugging), which it uses during rendering. For example:
<ImagesList images={[
{ title: 'Image 1', description: 'Image 1 description', url: 'http://cssdeck.com/uploads/media/items/3/3yiC6Yq.jpg' },
{ title: 'Image 2', description: 'Image 2 description', url: 'http://cssdeck.com/uploads/media/items/4/40Ly3VB.jpg' },
{ title: 'Image 3', description: 'Image 3 description', url: 'http://cssdeck.com/uploads/media/items/0/00kih8g.jpg' },
]}/>
ImagesList renders an ImageItem component for each image. This is what this component looks like:
import React from 'react';
import { RIEInput, RIETextArea } from 'riek';
export class ImageItem extends React.Component {
constructor(props) {
super(props);
this.placeholder = {
title: 'Title',
description: 'Description',
};
this.value = this.props.image;
}
render() {
return (
<div className="list-group-item">
<div className="text-content">
<h4>
<RIEInput
className="description"
value={this.value.title.length <= 0 ?
this.placeholder.title : this.value.title}
change={(item) => {
this.value.title = item.value;
this.props.onChange(this.value);
}}
validate={(value) => value.length >= 1}
classEditing="form-control"
propName="value"
/>
</h4>
<span>
<RIETextArea
className="description"
value={this.value.description.length <= 0 ?
this.placeholder.description : this.value.description}
change={(item) => {
this.value.description = item.value;
this.props.onChange(this.value);
}}
validate={(value) => value.length >= 1}
classEditing="form-control"
propName="value"
rows="2"
/>
</span>
</div>
<img className="thumb img-responsive"
style={{width: '20%' }}
src={this.value.url}
alt="Image"
data-action="zoom"
/>
{this.props.deletable ?
<div className="delete-btn">
<span onClick={this.props.onDelete}>
×
</span>
</div>
:
undefined }
</div>
);
}
}
Let's say I have three images, image A, B and C, and I want to delete image B. After pressing the delete button, image C will disappear from the GUI instead.
Inside the deleteImage() function of ImagesList, I am logging the index that is to be deleted and also log the values before and after the deletion. The index that is logged is correct, in this case that is index 1. Before the deletion the values are images A, B and C. After deletion the values are images A and C, as they should be.
I decided to do some logging inside the render() function of ImagesList as well. Unfortunately this also logs the correct values A and C, but A and B are actually rendered.
I have also tried to use the React state for this component instead of storing it in a local variable in conjunction with forceUpdate().
Another thing I have tried is to use the React Developer Tools plugin for Chrome. The Devtools also show the correct values, but the GUI still does not, as shown in this screenshot.
I'm currently out of ideas on what to try, any help would be appreciated!
Using the snippets I provided, you should be able to create a Meteor project and reproduce this bug.
With MasterAM's suggestion I managed to find two different solutions.
A.) Using componentWillUpdate()
The this.value variable is set only once namely in the constructor of the ImageItem component. To ensure that changes are properly delegated, you have to update this.value inside the componentWillUpdate() function. Something like:
componentWillUpdate(nextProps, nextState) {
this.value = nextProps.image;
}
B.) Using the property directly
This is definitely the more proper solution. Here we get rid of the local variable this.value inside the constructor of the ImageItem component.
Inside the render() function you replace this.value with this.props.image. Now without having to use the componentWillUpdate() function, everything works as expected.

Removing item in Firebase with React, re-render returns item undefined

I am building an app to learn React, and am using Firebase as my data storage. I have all the items in my firebase rendering out, and am trying to enable removal of individual items. However, when I try to remove, I get Uncaught TypeError: Cannot read property 'name' of null after clicking on the remove button, and it is referring to this line of code in the renderExpenditure function:
<strong>{details.name}</strong>, <strong>{h.formatPrice(details.amount)}</strong>, {details.category}, {details.type}, {details.date}
The state is set up as follows:
getInitialState: function() {
return {
cashbook: {
expenditure: {},
income: {}
},
totals: {},
available: {}
}
}
And the functions which render out the items and try to remove them are as follows:
(Can anyone see what I am doing wrong, or is this too little code to work out what is going on?)
Thanks in advance.
Within App
render: function() {
return(
<div className="cashbook">
<div className="expenditure">
<ul className="items">
{Object.keys(this.state.cashbook.expenditure).map(this.renderExpenditure)}
</ul>
</div>
</div>
);
}
renderExpenditure: function(key) {
var details = this.state.cashbook.expenditure[key];
return(
<li className="item" key={key}>
<strong>{details.name}</strong>, <strong>{h.formatPrice(details.amount)}</strong>, {details.category}, {details.type}, {details.date}
<button className="remove-item" onClick={this.removeExpenditure.bind(null, key)}>Remove</button>
</li>
);
},
removeExpenditure: function(key) {
this.state.cashbook.expenditure[key] = null;
this.setState({
expenditure: this.state.cashbook.expenditure
});
},
You are setting the wrong value in setState. expenditure doesn't exist in the root state, so you must overwrite the parent that contains it. It should be:
this.state.cashbook.expenditure[key] = null;
this.setState({
cashbook: this.state.cashbook
});

Categories