Motion: [BUG] motion.custom does not recognize reach-ui components

Created on 17 Dec 2019  路  4Comments  路  Source: framer/motion

2. Describe the bug

When using reach-ui components, motion.custom is unable to find component refs, despite the library properly exposing forwardRef. I suspect that this has something to do with Reach鈥檚 implementation of useForkedRef, which allows them to support internal and passed refs at the same time. It looks like useForkedRef assigns refs lazily, so it鈥檚 possible that motion is simply too eager in validating refs?

Happy to cross-reference this with Reach if you think this is an implementation issue on their end.

3. IMPORTANT: Provide a CodeSandbox reproduction of the bug

CodeSandbox minimal reproduction will be added shortly.

4. Steps to reproduce

Steps to reproduce the behavior:

  1. Install @reach/tooltip
  2. Render a wrapped motion.custom(TooltipPopup)
  3. See console error
Error: Uncaught [Error: No 'ref' found. Ensure components created with motion.custom forward refs using 'React.forwardRef']

5. Expected behavior

I expect motion.custom to resolve the correct ref, since their ref assignment is handled properly by React, albeit lazily.

bug

Most helpful comment

I'm having the same issue, any workaround for the moment?

All 4 comments

Possibly related to #410?

I'm having the same issue, any workaround for the moment?

Same issue here with the DialogOverlay component from @reach/dialog which also uses the useForkedRef hook

I have a workaround, but I'm not proud of it 馃槄 I edited the built output for framer motion to run an async loop that resolves when ref.current is an element:

function delayPromise(ms) {
    return new Promise(resolve=>setTimeout(resolve,ms))
}

async function waitForElement(getValue) {
    while (true) {
        const value = getValue()
        if (value instanceof Element) return value
        await delayPromise(50)
    }
}

/**
 * `useEffect` gets resolved bottom-up. We defer some optional functionality to child
 * components, so to ensure everything runs correctly we export the ref-binding logic
 * to a new component rather than in `useMotionValues`.
 */
var MountComponent = function (_a) {
    var ref = _a.innerRef, values = _a.values, isStatic = _a.isStatic;
    useEffect(function () {
        waitForElement(() => ref.current).then(() => {
            var domStyler = styler(ref.current, {
                preparseOutput: false,
                enableHardwareAcceleration: !isStatic,
            });
            values.mount(function (key, value) {
                domStyler.set(key, value);
                if (syncRenderSession.isOpen()) {
                    syncRenderSession.push(domStyler);
                }
            });
        })
        return function () { return values.unmount(); };
    }, []);
    return null;
};

So it seems @natemoo-re is right, that framer is evaluating the ref too eagerly. For the time being, I'm using patch-package to keep this change intact

Was this page helpful?
0 / 5 - 0 ratings