首页 > 解决方案 > setState 不会在函数范围之外保持状态

问题描述

firebase 存储 API 参考中的 listFilesAndDirectories 函数用于获取已保存在存储桶中的图像的下载 URL,但使用 setState 将状态保留为 url,直到作为图像 uri 传递不起作用

import React, {setState} from 'react';
import {DrawerActions, useNavigation} from '@react-navigation/native';

const Gallery: () => React$Node = () => {
  const navigation = useNavigation();

  function listFilesAndDirectories(reference, pageToken) {
    return reference.list({pageToken}).then(result => {
      result.items.forEach(ref => {
        // call getDownloadURL on every object reference
        ref.getDownloadURL().then(url => {
          setState({url: url});
          console.log(
            `File is referenced from :\n ${storageReference}:\n\n Image URL is:\n ${url}`,
          );
          console.log(`url is available here ${url}`);
           //after this point URL is no longer available
        });
      });

      if (result.nextPageToken) {
        return listFilesAndDirectories(reference, result.nextPageToken);
      }

      return Promise.resolve();
    });
  }

  const storageReference = firebase
    .storage()
    .refFromURL('gs://app404.com/images');

  listFilesAndDirectories(storageReference).then(() => {
    console.log('Started listing image download urls');
  });

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <View style={SharedStyles.header}>
          <TouchableOpacity
            onPress={() => {
              navigation.dispatch(DrawerActions.openDrawer());
            }}>
            <Hamburg />
          </TouchableOpacity>
          <Header title="Gallery" />
        </View>
        <ScrollView contentInsetAdjustmentBehavior="automatic">
          <View style={styles.container}>
            <View style={styles.sectionContainer}>
              <View>
                <Text>
                  Welcome to the Abulad Gallery! Come back for more content....
                </Text>
                <Image
                  source={{
                    uri: {url}//url is not defined
                  }}
                  style={styles.fetchedImage}
                />
              </View>
            </View>
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

需要一种即使在函数之外也能保持状态的方法,以便可以显示图像。谢谢

标签: javascriptfirebasereact-nativefirebase-storage

解决方案


您正在尝试使用钩子而没有真正正确地使用它们。useState是 React 中的一个初始化函数,它本质上是创建一个初始状态并在 React 执行堆栈中创建一个引用,当set*调用返回的函数时,节点应该重新渲染。

这是你如何使用useState

import { useState} from "react";

const Gallery: () => {
  const navigation = useNavigation();
  const [ downloadUrl, setDownloadUrl ] = useState({ url: undefined });
  console.log(downloadUrl);
  // should be {url: undefined} on 1st render, {url: <some url>} on subsequent renders

  // This should be refactored to `useEffect` to take advantage of async operations in React
  function listFilesAndDirectories(reference, pageToken) {
    return reference.list({pageToken}).then(result => {
      result.items.forEach(ref => {
        // call getDownloadURL on every object reference
        ref.getDownloadURL().then(url => {
          // An array would be better here, but this is just for example
          setDownloadUrl({ url }); // creates obj {url: url}, will cause a re-render once execution is complete
          console.log(
            `File is referenced from :\n ${storageReference}:\n\n Image URL is:\n ${url}`,
          );
          console.log(`url is available here ${url}`);
           //after this point URL is no longer available
        });
      });

      if (result.nextPageToken) {
        return listFilesAndDirectories(reference, result.nextPageToken);
      }

      return Promise.resolve();
    });
  }

  // ...

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <View style={SharedStyles.header}>
          <TouchableOpacity
            onPress={() => {
              navigation.dispatch(DrawerActions.openDrawer());
            }}>
            <Hamburg />
          </TouchableOpacity>
          <Header title="Gallery" />
        </View>
        <ScrollView contentInsetAdjustmentBehavior="automatic">
          <View style={styles.container}>
            <View style={styles.sectionContainer}>
              <View>
                <Text>
                  Welcome to the Abulad Gallery! Come back for more content....
                </Text>
                <Image
                  source={{
                    uri: { downloadUrl.url }//url is not defined
                  }}
                  style={styles.fetchedImage}
                />
              </View>
            </View>
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
}

完成该工作后,您可以更新图像占位符以使用微调器图标或其他加载图标,例如。

{ !downloadUrl.url ? <Spinner /> : (
  <div>...</div>
)}

推荐阅读