它与useEffect 无关。你打电话给retrieveItemStatus无条件每次调用组件函数来渲染组件时。 retrieveItemStatus 调用 updateStatuses 其中改变状态.您会看到 useEffect 回调作为其副作用重复运行,因为您的 useEffect 回调将 itemStatuses 作为依赖项。
我假设您只需要itemStatuses 即可获取一次。如果是这样,请将调用放入带有空依赖数组的 useEffect 回调中:
useEffect(retrieveItemStatus, []);
另外,你有(注意***):
const App = () => {
var items // ***
// ...
useEffect(() => {
const copyData = async () => {
// ...
items = itemsCopy; // ***
// ...
};
copyData();
}, [itemStatuses]);
};
那是行不通的,当你从回调中分配给items 时,你可能试图用items 做的任何事情都已经使用了undefined(当你不给时它得到的值它一个)。如果您需要保留items,请将其置于状态(如果您将其用于渲染)或参考(如果您不使用)。
你在评论中说:
好的,所以我将 retrieveItemStatus() 调用放入 useEffect 并删除了修复循环的依赖项。但是现在有一个问题,在调用 copyData() 并且需要 itemStatuses 之前,itemStatuses 状态没有得到更新。所以它不会做任何事情,直到我再次手动刷新/渲染整个事情。
如果copyData 依赖于来自retrieveItemStatus 的结果,那么将对它们中的每一个的调用放在相同的useEffect,在您从 retrieveItemStatus 获得结果之前不要调用 copyData。类似于以下内容,尽管您当然需要对其进行调整,因为我没有所有详细信息(我还进行了一些其他的 cmets 并在其中进行了标记):
// *** 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`
useEffect(() => {
(async () => {
const newStatuses = await retrieveItemStatus();
const newItems = 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>
);
};
以下是这些链接作为链接: