我的程序进入无限循环,每次启动应用程序时都会不断调用useEffect().我有一个状态,我不认为它会发生变化,但它会发生变化,所以我不明白为什么它会像现在这样进入循环.

const App = () => {
  var items;
  const [itemStatuses, updateStatuses] = useState({});

  const retrieveItemStatus = async () => {
    var tempStatuses;
    try {
      const value = await AsyncStorage.getItem("@item_Statuses");
      if (value !== null) {
        tempStatuses = await JSON.parse(value);
        //console.log("123456");
      } else {
        tempStatuses = await JSON.parse(
          JSON.stringify(require("../default-statuses.json"))
        );
      }
      updateStatuses(tempStatuses);
    } catch (error) {}
  };
  retrieveItemStatus();

  useEffect(() => {
    const copyData = async () => {
      const itemsCopy = [];

      const coll = await collection(db, "Items");
      const querySnapshots = await getDocs(coll);
      const docsArr = querySnapshots.docs;
      docsArr.map((doc) => {
        var data = doc.data();
        if (itemStatuses[data.name] === "locked") return;
        itemsCopy.push(data);
      });
      items = itemsCopy;
      //getItems([...itemsCopy]);
    };

    copyData();

  }, [itemStatuses]);

  return (
    <View style={styles.container}>
      <Text>temp.......</Text>
    </View>
  );
};

推荐答案

这与useEffect无关.每次调用组件函数来呈现组件时,都会调用retrieveItemStatus unconditionally.retrieveItemStatusupdateStatuses哪个叫changes state.您会看到useEffect回调被重复运行作为一个副作用,因为您的useEffect回调有itemStatuses作为依赖项.

我想你只需要itemStatuses美元就能拿到一次.如果是,则将调用放在useEffect回调中,并使用空的依赖项数组:

useEffect(retrieveItemStatus, []);

此外,您还拥有(请注意***):

const App = () => {
  var items // ***
  // ...
  useEffect(() => {
    const copyData = async () => {
      // ...

      items = itemsCopy; // ***

      // ...
    };

    copyData();

  }, [itemStatuses]);
};

这不会起作用,当您从回调中将items赋给items时,您可能一直试图用items做的任何事情都已经使用了undefined(当您没有给它赋值时,它得到的值).如果您需要保留items,则将其置于状态(如果您将其用于渲染)或处于Ref状态(如果不是这样).


在你的 comments 中,你说:

好的,我在useEffect中放了retrieveItemStatus()个调用,并移除了修复循环的依赖项.但现在有一个问题,在调用copyData()之前itemStatuses状态不会更新,而itemStatuses是需要的.因此,在我再次手动刷新/渲染整个场景之前,它不会执行任何操作.

如果copyData依赖于retrieveItemStatus的结果,则将对它们中的每一个的调用放在same useEffect中,在获得retrieveItemStatus的结果之前不调用copyData.类似于下面的内容,当然您需要对其进行调整,因为我没有所有的细节(我还在其中做了一些其他的 comments 和更改,我已经标记了):

// *** There's no need to recreate this function on every render, just
// have it return the information
const retrieveItemStatus = async () => {
    try {
        let tempStatuses; // *** Declare variables in the innermost scope you can
        const value = await AsyncStorage.getItem("@item_Statuses");
        if (value !== null) {
            tempStatuses = await JSON.parse(value);
            //console.log("123456");
        } else {
            // *** stringify + parse isn't a good way to copy an object,
            // see your options at:
            // https://stackoverflow.com/questions/122102/
            tempStatuses = await JSON.parse(JSON.stringify(require("../default-statuses.json")));
        }
        return tempStatuses;
    } catch (error) {
        // *** Not even a `console.error` to tell you something went wrong?
    }
};

// *** Similarly, just pass `itemStatuses` into this function
const copyData = async (itemStatuses) => {
    const coll = await collection(db, "Items");
    const querySnapshots = await getDocs(coll);
    const docsArr = querySnapshots.docs;
    // *** Your previous code was using `map` just as a loop,
    // throwing away the array it creates. That's an anti-
    // pattern, see my post here:
    // https://thenewtoys.dev/blog/2021/04/17/misusing-map/
    // Instead, let's just use a loop:
    // (Alternatively, you could use `filter` to filter out
    // the locked items, and then `map` to build `itemsCopy`,
    // but that loops through twice rather than just once.)
    const itemsCopy = [];   // *** I moved this closer to where
                            // it's actually filled in
    for (const doc of docsArr) {
        const data = doc.data();
        if (itemStatuses[data.name] !== "locked") {
            itemsCopy.push(data);
        }
    }
    //getItems([...itemsCopy]); // *** ?
    return itemsCopy;
};

const App = () => {
    // *** A new `items` is created on each render, you can't just
    // assign to it. You have to make it a member of state (or use
    // a ref if it's not used for rendering.)
    const [items, setItems] = useState(null);
    const [itemStatuses, setItemStatuses] = useState({});
    // ***               ^−−−−− the standard convention is `setXyz`.
    // You don't have to follow convention, but it makes it easier
    // for other people to read and maintain your code if you do.

    useEffect(() => {
        (async () => {
            const newStatuses = await retrieveItemStatus();
            const newItems = await copyData(newStatuses);
            // *** Do you need `itemStatuses` to be in state at all? If it's
            // only used for calling `copyData`, there's no need.
            setItemStatuses(newStatuses);
            setItems(newItems);
        })().catch((error) => {
            console.error(error);
        });
    }, []);

    // *** You didn't show what you're using here, so it's hard to be
    // sure what needs to be in state and what doesn't.
    // Only put `items` or `itemStatuses` in state if you use them for
    // rendering.
    return (
        <View style={styles.container}>
            <Text>temp.......</Text>
        </View>
    );
};

以下是作为链接的那些链接:

Javascript相关问答推荐

如何将剧作家请求对象转换为字符串,因为它只提供promise ?

为什么新的Promises会在不需要的情况下被用来等待?

如果使用React Select ,如何将CSS类添加到元素中?

输入有关HTML复选框的已判断属性的信息

除了在Angular 16中使用快照之外,什么是可行且更灵活的替代方案?

如何循环访问对象数组并以关键值形式获得结果?

如何让\w token 在此RegEx中表现得不贪婪?

使用useParams路由失败

React对话框模式在用户单击预期按钮之前出现

if/else JavaScript中的条件行为

在服务器上放置了Create Reaction App Build之后的空白页面

实现JS代码更改CSS元素

如何在bslib nav_insert之后更改导航标签的CSS类和样式?

我正在建立一个基于文本的游戏在react ,我是从JS转换.我怎样才能使变量变呢?

查询参数中的JAVASCRIPT/REACT中的括号

如何使用JS创建一个明暗功能按钮?

使用Ace编辑器对子组件实例的native-element 进行Angular 获取时面临的问题

为列表中的项目设置动画

用另一个带有类名的div包装元素

MongoDB通过数字或字符串过滤列表