使用 react suspense 从 Firebase 异步加载

Posted

技术标签:

【中文标题】使用 react suspense 从 Firebase 异步加载【英文标题】:Using react suspense to load asynchronously from Firebase 【发布时间】:2021-08-17 08:45:38 【问题描述】:

我正在尝试使用 suspense 从 Firebase 存储加载 GLTF 模型。为此,我需要先使用 getDownloadURL 方法异步获取模型的 URL,然后才能加载它。我看到的是加载程序被反复调用,但从未使用过响应 - 我确定我错过了一些简单的东西..

我已将代码放入此Code Sandbox,它使用了在互联网上似乎相当常见的示例,我已替换了 firebase 访问器(因为它需要私有访问密钥才能按预期工作)但替换功能是超时后返回一个 url 相当简单。

总结一下沙盒,如果它是一个获取下载url的函数,它包裹在一个挂起函数中:

function getModelData(path) 
    const storage = firebase.storage();
    const urlPromise = storage.ref(path).getDownloadURL();

    return  url: suspend(urlPromise) ;

这在我的代码中是这样使用的:

export default function Model(props) 
    const modelData = getModelData(props.path);
    const gltf = useGLTF(modelData.url.read());

    return (
        <mesh rotation=props.rotation position=props.position scale=props.scale>
            <primitive object=gltf.scene.clone(true) dispose=null/>
        </mesh>
    );

suspend函数正确地抛出了它的promise,promise解析设置结果,但是suspend函数本身被不断调用,result方法总是未定义。

【问题讨论】:

【参考方案1】:

getDownloadURL 是异步的,因此您需要像这样编写函数:

async function getModelData(path)
    const storage = firebase.storage();
    const urlPromise = await storage.ref(path).getDownloadURL();

    return  url: suspend(urlPromise) ;


【讨论】:

感谢您花时间回答 - 我认为这种模式的重点是 getModelData 返回的承诺被传递给挂起包装器,以便可以使用其 then 方法和挂起的承诺等待它扔回堆栈。我不相信让它异步并等待它在这里有帮助。 是的,但您不会将其作为承诺返回。那就是问题所在。你返回一个对象,该对象在 promise 中的某个位置。【参考方案2】:

好的,所以我继续调查并得出结论,这是行不通的 - 我遇到的问题是我不断重新创建包装器而不是创建一次并让承诺完成并更改吊挂器状态.我尝试切换到在 useEffect 挂钩中设置状态,并且仅在设置了状态时才加载 gltf,但这会导致渲染时挂钩数量不一致的错误。

相反,我使用状态来设置模型 URL 并将渲染模型放入第二个类:

function ModelFromUrl(props) 
    const gltf = useGLTF(props.url);

    return (
        <mesh rotation=props.rotation || [0, 0, 0] position=props.position || [0, 0, 0] scale=props.scale || [1, 1, 1]>
            <primitive object=gltf.scene.clone(true) dispose=null/>
        </mesh>
    );

export default function Model(props) 
    const [url, setUrl] = useState();

    useEffect(() => 
        firebase.storage().ref(props.path).getDownloadURL().then(url => setUrl(url));
    , [props.path]);
    if (! url) 
        return null;
    
    return <ModelFromUrl ...props url=url/>

让悬念机制发挥作用本来不错,但似乎这种模式就足够了。

【讨论】:

以上是关于使用 react suspense 从 Firebase 异步加载的主要内容,如果未能解决你的问题,请参考以下文章

React18: 如何使用Suspense 等待数据异步加载

如何在 React 中将 useEffect 与 Suspense 一起使用?

React Router 和 Suspense 的 404 路由问题

react懒加载(lazy, Suspense)

React Suspense 尝鲜,处理前后端IO异步操作

react懒加载(lazy, Suspense)