Использование ожидания реакции для асинхронной загрузки с Firebase

# #reactjs #concurrency #firebase-storage #react-suspense

Вопрос:

Я пытаюсь использовать приостановку для загрузки модели GLTF из хранилища Firebase. Для этого мне нужно сначала асинхронно получить URL-адрес модели с помощью метода getDownloadURL, прежде чем я смогу ее загрузить. Я вижу, что загрузчик неоднократно вызывается, но ответ никогда не используется — я уверен, что пропустил что-то простое..

Я поместил код в эту песочницу кода, в ней используются примеры, которые кажутся довольно распространенными в Интернете, я заменил средство доступа 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>
    );
}
 

Функция приостановки правильно выдает свое обещание, и обещание разрешает результат настройки, но сама функция приостановки постоянно вызывается, а метод результата всегда не определен.

Ответ №1:

Это асинхронно getDownloadURL , поэтому вам нужно будет написать свои функции следующим образом:

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

    return { url: suspend(urlPromise) };
}

 

Комментарии:

1. Спасибо, что нашли время ответить — я думаю, что смысл этого шаблона в том, что обещание, возвращаемое getModelData, передается в оболочку приостановки, чтобы его можно было дождаться, используя его метод then с приостановленным обещанием, возвращенным в стек. Я не верю, что асинхронность и ожидание здесь помогают.

2. Да, но вы не возвращаете это как Обещание. В этом-то и проблема. Вы возвращаете туда объект, в котором есть что-то внутри обещания.

Ответ №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}/>
}
 

Было бы неплохо заставить механизм ожидания работать, но, похоже, этого шаблона должно быть достаточно.