这与useEffect
无关.每次调用组件函数来呈现组件时,都会调用retrieveItemStatus
unconditionally.retrieveItemStatus
叫updateStatuses
哪个叫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>
);
};
以下是作为链接的那些链接: