hxf31891 / react-gradient-color-picker

An easy to use color/gradient picker for React.js
MIT License
126 stars 39 forks source link

Maximum update depth exceeded #71

Closed tabl-lofton closed 7 months ago

tabl-lofton commented 7 months ago

I realize this may be a duplicate 24, however I'm running into the same issue. The only reason I believe mine could be different is I'm using the color picker to change the appearance of the background of a component. For example, something like below would cause the error to occur.

const card = (props) => {
    const [color, setColor] = useState('rgba(255,255,255,1)')

    return (
      <>
        <Box sx={{backgroundColor: color}}>
          some content
        </Box>

        <ColorPicker
          value={color}
          onChange={setColor}
        />
      </>

Maybe I'm just dumb and can't figure it out, but this is the error I keep encountering, and I'm unsure as to how to proceed

hxf31891 commented 7 months ago

@tabl-lofton where are you importing the component from?

tabl-lofton commented 7 months ago

@tabl-lofton where are you importing the component from?

In my example, that’s just an MUI Box component. This is a very simplified example of how I’m actually using it, but the process is generally the same

hxf31891 commented 7 months ago

Interesting, I can't reproduce. Mac or PC? Chrome?

tabl-lofton commented 7 months ago

Interesting, I can't reproduce. Mac or PC? Chrome?

On PC, and chrome. I will add more code to give better perspective as to why what’s happening is happening. My guess is that what’s happening is, when the state is changing, it’s causing the component to re-render, which is causing that error to occur if I drag the color selector around the color picker. If I look in chrome dev tools, I can see that it keeps appending a style tag to the head as I keep dragging the color selector around. But as I said, I will get more code to provide further context ASAP

hxf31891 commented 7 months ago

Thanks @tabl-lofton, could you also send me a screenshot of the error? Does it happen on render or after user the component for a bit?

tabl-lofton commented 7 months ago

Thanks @tabl-lofton, could you also send me a screenshot of the error? Does it happen on render or after user the component for a bit?

It usually happens after a user moves it around a bit, and I'm at work, so I'm unable to get the specific error for you currently, however I will do so ASAP, however, as you mentioned, I'm trying to replicate it at work, and I'm having trouble doing so as well. To provide further context, The color is within an object that an image is also saved in. These two pieces of data from the object are used to update the appearance of a custom card I made, with the image being the background, and the color being used to create a gradient on top of the card. However, there's a lot more data besides those two pieces of information being updated, which leads me to believe there might be something else going on in my code that's causing this issue.

This is just to check and see if maybe you could identify with what limited information I can give you currently, but there is the parent component, and two child components. The function that changes the color is in the parent component, and is passed as a prop to child 1. In child 1, the color picker is there, which, as mentioned earlier, changes the color of the linear gradient which is on top of the card. The card itself is child 2 of the parent component in this example. The parent component is a page itself. Because I'm changing the state on the page itself, could that be the issue? I hope all of this makes sense, and I will once again provide more details when I am able to

tabl-lofton commented 7 months ago

Capture Here is the error I'm seeing.

However, I've identified the true issue, which does not have to do with the color picker, but more so with my still limited understanding of React/JS. There is a useEffect inside the same component that the colorPicker is in, and that useEffect is used for animation purposes for pieces of my form sliding in. That useEffect is dependant on props also from the parent component. Because I am changing a state in the parent component using the color picker, it causes everything to re-render, and BECAUSE, if I slide the color selection around, it mimics being stuck in an infinite loop interestingly enough. Thus, there isn't a bug, just a bit of interesting action happening with the code. I will provide the entire code of that component in this comment, just so people can see it and possibly use it as reference as to WHY it might be happening for them in the future.

import { useEffect, useRef, useState } from 'react'
import { Box, Button, Grid, Menu, MenuItem, Slide, Tooltip, Typography } from '@mui/material'
import ColorPicker from 'react-best-gradient-color-picker'
import PaletteIcon from '@mui/icons-material/Palette'
import ImageIcon from '@mui/icons-material/Image';

const EventCardDesign = (props) => {
  const ref = useRef()
  const { eventData, handleStep, isReturning, slideIndex, setSlideIndex, handleFileChange, handleColorChange } = props
  const [contextMenu, setContextMenu] = useState(null)

  const handleFileClick = () => {
    ref.current.click()
  }

  useEffect(() => {
    const startAnimation = () => {
      const animationTimeouts = [0, 250, 500, 750]
      animationTimeouts.forEach((timeout, index) => {
        setTimeout(() => {
          setSlideIndex(index + 1)
        }, timeout)
      })
    }

    if (!isReturning) {
      startAnimation()
    }
  }, [isReturning])

  const handleContextMenu = (event) => {
    event.preventDefault()
    setContextMenu({
      mouseX: event.clientX + 2,
      mouseY: event.clientY - 6
    })
  }

  const calculateAnchorPosition = (contextMenu) => {
    if (contextMenu !== null) {
      return {
        top: contextMenu.mouseY - 6,
        left: contextMenu.mouseX + 2
      }
    } else {
      return undefined
    }
  }

  const handleCloseContextMenu = () => {
    setContextMenu(null)
  }

  return (
    <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}>
      <Slide in={slideIndex >= 1} direction='right'>
        <Typography variant='h4' sx={{ textAlign: 'center', marginBottom: '20px' }}>
          Design your card here!
        </Typography>
      </Slide>
      <Grid container sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '10px' }}>
        <Slide in={slideIndex >= 2} direction='right'>
          <Tooltip title={`Click here to change your event card's image!`}>
            <Grid item
              onClick={handleFileClick}
              sx={{
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                height: '125px',
                width: '125px',
                backgroundColor: '#E192FE',
                padding: '10px',
                borderRadius: '15px',
                cursor: 'pointer',
                '&:hover': {
                  boxShadow: '0px 0px 12px rgba(255, 255, 255, 1)'
                }
              }}
            >
              <ImageIcon sx={{ scale: '3' }} />
              <input
                type='file'
                accept='image/*'
                style={{ display: 'none' }}
                ref={ref}
                onChange={handleFileChange}
              />
            </Grid>
          </Tooltip>
        </Slide>
        <Slide in={slideIndex >= 3} direction='right'>
          <Tooltip title={`Click here to change your event card's styling!`}>
            <Grid item
              onClick={handleContextMenu}
              sx={{
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                height: '125px',
                width: '125px',
                backgroundColor: `${eventData.cardColor}`,
                cursor: 'pointer',
                borderRadius: '125px',
                '&:hover': {
                  boxShadow: '0px 0px 12px rgba(255, 255, 255, 1)'
                }
              }}
            >
              <PaletteIcon sx={{ scale: '3' }} />
            </Grid>
          </Tooltip>
        </Slide>
        <Menu
          open={contextMenu !== null}
          onClose={handleCloseContextMenu}
          anchorReference='anchorPosition'
          anchorPosition={calculateAnchorPosition(contextMenu)}
        >
          <MenuItem disableRipple>
            <ColorPicker
              value={eventData.cardColor}
              onChange={handleColorChange}
              hidePresets
              hideOpacity
              hideControls
            />
          </MenuItem>
        </Menu>
      </Grid>
      <Slide in={slideIndex >= 4} direction='right'>
        <Button
          variant='contained'
          onClick={handleStep}
          sx={{ margin: '20px' }}
        >
          Continue
        </Button>
      </Slide>
    </Box>
  )
}

export default EventCardDesign
hxf31891 commented 7 months ago

@tabl-lofton thanks for the info!