skip to Main Content

I am trying to add all added images to a state, so I use spread operator.
I am not sure what I am doing wrong, since it just overwrites the previous value.
It seems that I am missing something.

codesandbox on line 43

useEffect(() => {
  if (addButton.current) {
      if (scene) {
        setImages([...allImages, newImg]); // <-- Here
      }
    });
  }
}, [addButton]);

2

Answers


  1. You need an addImage(newImage) function declared in S.js and you should pass that to M.js instead of setImages.

    Now in S.js have the following:

    const addImage = useCallback((newImage) => {
      setImages((existingImages) => {
        return [...existingImages, newImage];
      });
    }, [setImages]);
    
    <M
      allImages={allImages}
      addImage={addImage}
      addButton={addButtonRef}
      removeButton={removeButtonRef}
    />
    

    In M.js you have:

    useEffect(() => {
      if (addButton.current) {
          if (scene) {
            addImage(newImg); // Call when you want to add to `S`'s `allImages` state.
            setIndex((currentIndex) => currentIndex + 1); // Also, don't use `index`
          }
        });
      }
    }, [addButton, addImage]);
    

    Since S holds the state of the images, you need to essentially send a signal from M to S; to get S to update and cascade changes back down to M.

    The main takeaway here is that you should be using the function version of setState to get the current list of images, rather than the actual reference to allImages. If you use this inside of an effect, you will have an infinite loop.

    A quick search for "useState pitfalls" on Google will list a few helpful blog posts that will help you avoid these issues.

    Login or Signup to reply.
  2. To answer your question in short. Use functional state update to set the state. Like this:

    setIndex((index) => index++) // for indices
    setImages((prev) => [...prev, newImg]) // for images
    

    Let me go through some lines of your code.
    I see that you are forwarding refs to child component M from S. In that case, you need to follow the react way of forwarding ref. Currently, react is logging error for you to identify this issue in the console. Right way would be something like this for line number 8 – 15.

    const M = React.forwardRef(({ addButton, removeButton, allImages, setImages,selectedImg, moveButton },ref) => {
    // other 
    // other 
    // ...
    })
    

    React provides this reason to do so: This happens because by default React does not let a component access the DOM nodes of other components. Not even for its own children! This is intentional. Refs are an escape hatch that should be used sparingly. Manually manipulating another component’s DOM nodes makes your code even more fragile. By default React does not let a component access the DOM nodes of other components

    Likewise in your useEffect hook from line 27 to 62, there are some refactoring to do. Let me paste the code here and explain some changes in the comment section:

    useEffect(() => {
          let img;
          if (selectedImg) {
            img = new Image();
            img.src =
              // "https://fashion-desings.s3.ap-south-1.amazonaws.com/common/casuals/tshirts/Front-labels/t4.png";
              selectedImg;
            img.crossOrigin = "anonymous";
          }
    
          let listener = () => {
            if (scene) {
              if (img) {
                const mesh = scene.getMeshByName("insert");
                mesh.material = texMat;
    
                ctx.drawImage(img, 470.96392577671463, 580.6505813320091, 150, 150);
    
                tex.update();
    
                setIndex((ind) => ind + 1); // Try to use functional state update
    
                const newImg = {
                  id: index,
                  img: img.src,
                  x: 470.96392577671463,
                  y: 580.6505813320091,
                  size: 150
                };
                setImages((prev) => [...prev, newImg]); // Use functional state update
              }
            }
          };
    
          if (addButton.current) {
            addButton.current.addEventListener("click", listener);
          }
    
    // Make sure to remove event listener on onMount
          return () => {
            addButton.current.removeEventListener("click", listener);
          };
        }, [addButton, allImages, selectedImg]);
    

    That’s it. That would do it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search