Home > database >  Getting Uncaught RangeError: Maximum call stack size exceeded in React
Getting Uncaught RangeError: Maximum call stack size exceeded in React

Time:12-04

I'm a beginner with React and I have been coding a project for one of the courses in uni. However, I have been now struggling for quite some time with following error: Uncaught RangeError: Maximum call stack size exceeded. I have tried to find a solution with no luck.

Here is my code for the React component that causes the error:

`

import { Container } from "react-bootstrap"
import { useParams } from "react-router"
import apiService from "../services/apiService"
import { useEffect, useState } from "react"
import Spinner from 'react-bootstrap/Spinner';

const GOOGLE_API_KEY = process.env.REACT_APP_GOOGLE_API_KEY

export const TreeProfile = (props) => {
  const [tree, setTree] = useState({})
  const [fetching, setFetching] = useState(true)
  const [zoom, setZoom] = useState("12")
  const [location, setLocation] = useState({latitude: "", longitude: ""})

  let { id } = useParams()

  const handleZoomChange2 = (event) => {
    console.log(event.target.value)
    setZoom(event.target.value)
  }

  console.log("Called TreeProfile")

  useEffect(() => {
    console.log("id", id)
    apiService.getOne(id).then(t => {
      console.log("data", t)
      setTree(t)
      setLocation({latitude: t.location.latitude, longitude: t.location.longitude})
      setFetching(false)
    })
  }, [])

  return (
    <Container className="treeprofile-page">
      {
        fetching === false ?
        <img style={{height: '200px', width: '300px'}} src={`data:image/${tree.image.contentType};base64,${btoa(String.fromCharCode(...new Uint8Array(tree.image.data.data)))}`} alt='' />
        :
        <Spinner animation="border" variant="primary" />
      }
      
      <h1>{tree.name}</h1>
      {
        fetching === false ?
        <h3>Planted on {new Date(tree.createdAt).toDateString()}</h3>
        :
        null
      }
      <h3>Planted by {tree.user}</h3>

      {
        fetching === false ?
        <div>
        <img src={`https://maps.googleapis.com/maps/api/staticmap?center=${location.latitude},${location.longitude}&format=gif&zoom=${zoom}&size=300x200&markers=color:red|${location.latitude},${location.longitude}&key=${GOOGLE_API_KEY}`} alt='' />
        </div>
        :
        null
      }
      <div>
        <input className="m-3" type="range" min="1" max="16" value={zoom} onChange={handleZoomChange2} />
      </div>

      <button type="button" className="btn btn-primary" >Add update</button>
      
    </Container>
  )
}

`

The error happens every time I try to update the zoom level by sliding the slider, which calls handleZoomChange2 function that sets the state. I have other component in different route with the same functionality and it works fine. However, this one for some reason causes the error constantly.

The apiService fetches the data with axios from the backend, which in turn fetches the data from MongoDB.

I tried to update zoom level by sliding the slider input, which calls on handleZoomChange2 setting a new state to the zoom. However, the code throws an error Maximum call stack size exceeded. Please, help!

Error:

TreeProfile.js:38 Uncaught RangeError: Maximum call stack size exceeded
at TreeProfile (TreeProfile.js:38:1)
at renderWithHooks (react-dom.development.js:16305:1)
at updateFunctionComponent (react-dom.development.js:19588:1)
at beginWork (react-dom.development.js:21601:1)
at beginWork$1 (react-dom.development.js:27426:1)
at performUnitOfWork (react-dom.development.js:26557:1)
at workLoopSync (react-dom.development.js:26466:1)
at renderRootSync (react-dom.development.js:26434:1)
at recoverFromConcurrentError (react-dom.development.js:25850:1)
at performSyncWorkOnRoot (react-dom.development.js:26096:1)

CodePudding user response:

In case somebody stumbles upon this, here's the solution:

Cause of the problem

Encoding of the image from array buffer tree.image.data.data to base64 was causing the error of Uncaught RangeError: Maximum call stack size exceeded. I'm not quite certain why exactly, somebody smarted than me may explain it.

<img style={{height: '200px', width: '300px'}} src={`data:image/${tree.image.contentType};base64,${btoa(String.fromCharCode(...new Uint8Array(tree.image.data.data)))}`} alt='' />

Solution

I found the solution to this problem from this Stackoverflow thread. There they discuss the problem more in detail, however, here is the solution that worked for me.

  const _arrayBufferToBase64 = ( buffer ) => {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i  ) {
        binary  = String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
  }

here the buffer is tree.image.data.data (the array buffer of unit8array) and the function returns base64 encoded result. My code after these changes looks following

import { Container } from "react-bootstrap"
import { useParams } from "react-router"
import apiService from "../services/apiService"
import { useEffect, useState } from "react"
import Spinner from 'react-bootstrap/Spinner';

const GOOGLE_API_KEY = process.env.REACT_APP_GOOGLE_API_KEY

export const TreeProfile = (props) => {
  const [tree, setTree] = useState(null)
  const [fetching, setFetching] = useState(true)
  const [zoom, setZoom] = useState("12")
  const [location, setLocation] = useState({latitude: "", longitude: ""})

  let { id } = useParams()

  console.log("Called TreeProfile")
  console.log("fetching", fetching)

  useEffect(() => {
    console.log("id", id)
    apiService.getOne(id).then(t => {
      console.log("data", t)
      setTree(t)
      setLocation({latitude: t.location.latitude, longitude: t.location.longitude})
      setFetching(false)
    })
  }, [])
  
  const handleZoomChange2 = (event) => {
    console.log(event.target.value)
    setZoom(event.target.value)
  }
  const test = (event) => {
    console.log(event.target.value)
    setFetching(!fetching)
  }

  const _arrayBufferToBase64 = ( buffer ) => {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i  ) {
        binary  = String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
  }


  if (tree) {
    return (
      <Container className="treeprofile-page">
        <div>
          <img style={{height: '200px', width: '300px'}} src={`data:image/${tree.image.contentType};base64,${_arrayBufferToBase64(tree.image.data.data)}`} alt='' />
          <h1>{tree.name}</h1>
          <h3>Planted on {new Date(tree.createdAt).toDateString()}</h3>
          <h3>Planted by {tree.user}</h3>
          </div>
        <img src={`https://maps.googleapis.com/maps/api/staticmap?center=${location.latitude},${location.longitude}&format=gif&zoom=${zoom}&size=300x200&markers=color:red|${location.latitude},${location.longitude}&key=${GOOGLE_API_KEY}`} alt='' />
        <div>
          <input className="m-3" type="range" min="1" max="16" value={zoom} onChange={handleZoomChange2} />
        </div>
  
        <button type="button" className="btn btn-primary" onClick={test} >Add update</button>
        
      </Container>
    )
  } else {
    return (
      <Container>
        <Spinner animation="border" variant="primary" />
      </Container>
    )
  }
  
}

Hope this helps anybody facing the same problems!

CodePudding user response:

the handleZoomChange2 method is calling itself in an infinite loop. try moving the setZoom call to the end of the function.

const handleZoomChange2 = (event) => {
  console.log(event.target.value)
  setZoom(event.target.value)
}

This will prevent the function from calling itself again before it has finished executing.

Also if you want to update the location state whenever the tree state is updated, you can add it to the dependency array in the useEffect hook.

useEffect(() => {
  console.log("id", id)
  apiService.getOne(id).then(t => {
    console.log("data", t)
    setTree(t)
    setLocation({latitude: t.location.latitude, longitude: t.location.longitude})
    setFetching(false)
  })
}, [tree])

This will cause the useEffect hook to run again whenever tree is updated.

  • Related