我在类中有一个方法,它返回 JSON 对象,然后在类之外的函数中创建此类的实例,调用其中一个方法并期望读取返回的 JSON 值。但发生的情况是,分配给该方法的变量在方法收集数据之前就被调用,导致未定义。我尝试过短路(&&),这以前对我有用,但这次不行。我还尝试使用 hooks 而不是 let 并返回它,这最终对我有用,但由于某种原因,它正在循环,就好像我把它放在 while(1) 中一样。所有方法均取自 SQLite 教程并进行了修改。
function Screen() {
const a = new App();
let prodReturned = null;
prodReturned = a.getProducts();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>text: {prodReturned && JSON.stringify(prodReturned)}</Text>
</View>
);
}
export default class App extends Component {
getProducts() {
let products=[];
this.listProduct().then((data) => {
products = data;
console.log(typeof(data),data,'this returns object type and correct json form')
return products;
}).catch((err) => {
console.log(err);
})
}
listProduct() {
return new Promise((resolve) => {
const favdrinks = [];
this.initDB().then((db) => {
db.transaction((tx) => {
tx.executeSql('SELECT p.favorites FROM drinks p', []).then(([tx, results]) => {
console.log("Query completed");
var len = results.rows.length;
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
console.log(`Drinks favorited: ${row.favorites}`)
const { favorites } = row;
favdrinks.push({
favorites
});
}
console.log(favdrinks);
resolve(favdrinks);
});
}).then((result) => {
this.closeDatabase(db);
}).catch((err) => {
console.log(err);
});
}).catch((err) => {
console.log(err);
});
});
}
}
如何确保在准备好后调用该方法并将返回值分配给变量?
最佳答案
React 组件必须同步渲染。这就是 React 的设计方式;它不能也永远不会改变。您听说过的任何异步渲染方式都是一种抽象,它会缓存旧的渲染结果并同步返回它们。因此,您将永远无法执行代码指示的操作。
但是,我怀疑您实际上并不关心该函数是否在数据加载之前被调用(这是您的代码所建议的),而是您希望调用它并显示加载菜单,直到异步操作完成并给你它的结果。这是 React 中的一个常见问题,可以通过稍后设置初始空状态来解决。
import React, { useState } from 'react';
function MyComponent() {
// Since you're using TypeScript, you can type this by
// setting the generic parameter
// e.g. useState<Product[] | null>(null);
const [products, setProducts] = useState(null);
listProducts().then(function(loadedProducts) {
setProducts(loadedProducts);
});
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>text: {products !== null && JSON.stringify(products)}</Text>
</View>
);
}
为了使其更像 ES6:
import React, { useState } from 'react';
const MyComponent = () => {
const [products, setProducts] = useState(null);
listProducts().then(setProducts);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>text: {products && JSON.stringify(products)}</Text>
</View>
);
}
这样做只有一个问题:每次调用 MyComponent
时,都会加载数据。在某些情况下您可能需要这样做,但在绝大多数情况下,您只想在发生变化时重新加载产品。这就是 useEffect
Hook 的用武之地。它接受一个仅在依赖项列表发生更改时才会被调用的函数。
useEffect(() => {
// This is called once on the first render and only
// called again whenever either dep1 or dep2 changes
// Note that a change in dep1 or dep2 does not necessarily
// mean your component will be called again (i.e. be
// rerendered). That only happens if dep1 or dep2 came
// from a useState hook, because to change a value from
// a useState hook, you must call the setter, which tells
// React to rerender.
console.log(dep1 + dep2);
}, [dep1, dep2]);
如果将当前分钟添加到依赖项列表中,则产品列表最多每分钟更新一次。
useEffect(() => {
listProducts().then(setProducts);
}, [
// Time in milliseconds rounded down to the minute
Math.floor(Date.now() / (60 * 1000))
]);
如果您只想调用该函数一次,请将依赖项列表清空。如果您想在每次渲染后调用它,则根本不要指定依赖项列表(即 useEffect(callback)
)。了解更多 here .
其他一些事情:您的 App
类毫无意义。您可能已经习惯了 Java 和 C# 等语言的面向对象编程,但现代 JavaScript 尽可能合理地避免使用类。此外,您不需要扩展 React.Component
因为您不需要使用 React 来渲染该类。我建议将函数移出类。此外,您似乎不确定 Promise 是如何工作的。它们是异步调用的,回调是在封闭函数完成后调用的,除非您使用 async/await。我会为你重构这个,但你真的不应该在没有基础知识的情况下接受如此困难的事情。试试this Promise guide首先,然后了解如何 async/await make it easy to avoid infinite .thens .
const getProducts = async () => {
const data = await listProducts();
// typeof isn't a function
console.log(typeof data, data);
}
const listProducts = async () => {
// Create the initDB() function the way I did this
const db = await initDB();
const favdrinks = await new Promise((resolve, reject) => {
db.transaction(async tx => {
const [tx, results] = await tx.executeSql(
'SELECT p.favorites FROM drinks p',
[]
);
const favdrinks = [];
console.log("Query completed");
var len = results.rows.length;
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
console.log(`Drinks favorited: ${row.favorites}`)
const { favorites } = row;
favdrinks.push({
favorites
});
}
console.log(favdrinks);
resolve(favdrinks);
})
});
// Refactor this function as well
await closeDatabase(db);
return favdrinks;
}
把它们放在一起:
import React, { useState, useEffect } from 'react';
const listProducts = async () => {
const db = await initDB();
return new Promise((resolve, reject) => {
db.transaction(async tx => {
const [tx, results] = await tx.executeSql(
'SELECT p.favorites FROM drinks p',
[]
);
const favdrinks = [];
var len = results.rows.length;
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
const { favorites } = row;
favdrinks.push({
favorites
});
}
resolve(favdrinks);
})
});
await closeDatabase(db);
return favdrinks;
}
const MyComponent = () => {
const [products, setProducts] = useState(null);
useEffect(() => {
listProducts().then(setProducts);
}, []); // empty dependency list means never update
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>text: {products && JSON.stringify(products)}</Text>
</View>
);
}
编辑:看到您对另一个答案的评论,您无法删除类逻辑。我认为您可能误解了您正在使用的任何框架的要求,但如果您严重无法避免它,您应该在 useEffect
回调中创建您正在使用的类的对象。
关于typescript - 从已解析的 Promise 返回 json 对象并在加载后分配它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65459599/