Search code examples
javascriptreactjsdeepar

Having an issue with making react component work inside a custom iframe component


So, I've basically tried everything with this one. I ran out of solutions or options. Thing is, I have a button. When you click on it your camera will open and you will see some filters that you can apply to your face. I am new to React. Made it work without the iframe to test the API first, but it's not working anymore inside this iframe. The react component needs to be inside this iframe. The code can be found here with what I did so far/tried: https://codesandbox.io/s/cool-fog-3k5si5?file=/src/components/button/button.jsx

The problem is that when I click the button, the canvas disappears from the page and I get this error in the console: enter image description here

The DeepAR API fails initialization because the canvas is no longer on the page and it crashes. I really don't know what to search for as I considered this to be a react render error and I tried different ways to write the react code (functional/class). If you have any ideas or suggestions, please help. Thank you in advance.


Solution

  • Your use of useEffect in your Modal and App Component is incorrect.

    To remind you, useEffect accepts a function which runs after the render is committed to the screen. If the function returns a function (which is your case), this function is the "clean up" function which is run before the component is removed from the UI.

    So what is happening is that your useEffect code is run only when your components are being unmounted.

    Since we are not concerned with any clean up at this stage, a quick solution for you is to move the clean up expressions to the main effect function as follows:

    useEffect(() => {
        fetch(
          "https://cors-anywhere.herokuapp.com/https://staging1.farmec.ro/rest/V1/farmec/deeparProducts/"
        )
          .then((response) => response.json())
          .then((productsJson) => setProducts(productsJson));
    }, []);
    

    The same goes for your Modal component :

    useEffect(() => {
        let initializedDeepAR = new DeepAR({
          licenseKey:
            "6fda241c565744899d3ea574dc08a18ce3860d219aeb6de4b2d23437d7b6dcfcd79941dffe0e57f0",
          libPath: DeepAR,
          deeparWasmPath: deeparWasm,
          canvas: canvas.current,
          segmentationConfig: {
            modelPath: segmentationMode
          },
          callbacks: {
            onInitialize: () => {
              // let filterName = colors[0].filterData[0]['Filter Binary Path'].match(new RegExp("[^/]+(?=\\.[^/.]*$)"))[0];
              setDeepAR(initializedDeepAR);
              initializedDeepAR.startVideo(true);
              // initializedDeepAR.switchEffect(0, 'slot', `https://staging1.farmec.ro/media/deepArFilters/${filterName}.bin`);
            }
          }
        });
    
        /*@TODO: replace paths with server local path*/
        initializedDeepAR.downloadFaceTrackingModel(models);
    }, []);
    

    With one additional fix concerning your use of useRef.

    To target the element behind the useRef, you must use the .current property.

    Finally, your Frame component is using useState to manage the mounting of the iframe. I would suggest using the useRef hook with a useState for your mountNode as follows:

    export const Frame = ({
                            children,
                            styleSelector,
                            title,
                            ...props
                          }) => {
      const contentRef = useRef(null)
      const [mountNode, setMountNode] = useState()
    
      useEffect(() => {
        setMountNode(contentRef.current.contentWindow.document.body)
      }, [])
      
      useEffect(() => {
        const win = contentRef.current.contentWindow
        const linkEls = win.parent.document.querySelectorAll(
          styleSelector
        )
        if (linkEls.length) {
          linkEls.forEach((el) => {
            win.document.head.appendChild(el)
          })
        }
      }, [styleSelector])
    
      return (
        <iframe title={title} {...props} ref={contentRef}>
          {mountNode && createPortal(children, mountNode)}
        </iframe>
      )
    }