【问题标题】:Firebase (Firestore) getting ref value async - ReactJSFirebase(Firestore)获取参考值异步 - ReactJS
【发布时间】:2019-11-22 16:54:40
【问题描述】:

我不得不承认,这是我在 TypeScript-JavaScript 中看到的最奇怪的错误(我在 TypeScript 中获得了 Model 类,在 JS 中获得了 ReactJS 组件...)。我有一个 Promo 对象的列表,其中一个属性“_listCompte”是 Compte 对象的列表。

Promo 列表是通过从数据库中检索数据创建的,此时一切正常

之后我在 listPromo 上得到了一个 forEach 并且它可以工作,但是当我尝试在 listCompte 上使用 forEach 进行迭代时 好像是空的。

ReactJS 组件:

class DettesView extends Component{
state = {
    loading: true,
    activeItem: "DI4",
    listPromo: []
};


componentDidMount() {
    let listPromo = [];
    FirebaseAPI.getDatabase().collection("Promo")
        .onSnapshot((docSnapshot)=>{
            docSnapshot.forEach((doc)=>{
                const promoData = doc.data();
                let promo = Promo.fromData({
                    nom: promoData.nom,
                    promoId: promoData.promoId
                });
                promoData.listCompte.forEach((compteRef)=>{
                    compteRef.get()
                        .then((doc)=>{
                            if(doc.exists){
                                let compte = Compte.fromData(doc.data());
                                promo.addCompte(compte);
                            }
                        }
                    )
                });
                listPromo.push(promo);
            });
            console.log("listPromo", listPromo);
            this.setState({
                loading: false,
                status: true,
                listPromo: listPromo
            });
        }, (error)=>{
            this.setState({
                loading: false,
                status: false,
                errorMessage: error.getMessage(),
                errorCode: error.getCode()
            });
        });
}

toggleTabs = tab => () => {
    if (this.state.activeTab !== tab) {
        this.setState({
            activeTab: tab
        });
    }
};

render() {
    const {loading, listPromo} = this.state;
    if(loading)
        return (
            <MDBContainer fluid>
                <MDBRow center={true}>
                    <MDBCol size="2" className="mt-5">
                        <MDBCard>
                            <MDBCardBody>
                                <img src={gifLoading} alt="gif-loading"/>
                                <h1 className="mt-4 text-center">Loading...</h1>
                            </MDBCardBody>
                        </MDBCard>
                    </MDBCol>
                </MDBRow>
            </MDBContainer>
        );
    else if(this.state.status)
        return (
            <div className="classic-tabs">
                <MDBNav classicTabs color="cyan">
                    {listPromo.map((value, index) => (
                        <MDBNavItem key={index}>
                            <MDBNavLink to="#" active={this.state.activeItem === value.nom} onClick={this.toggleTabs(value.nom)}>
                                {value.nom}
                            </MDBNavLink>
                        </MDBNavItem>
                    ))}
                </MDBNav>
                <MDBTabContent
                    className="card"
                    activeItem={this.state.activeItem}
                >
                    {listPromo.map((value, index) => {
                        console.log("value of property 'listCompte' in a 'Promo' object", value.listCompte);
                        console.log("size of property 'listCompte' in a 'Promo' object", value.listCompte.length);
                        return (
                            <MDBTabPane tabId={value.nom} key={index}>
                                <MDBTable hover>
                                    <MDBTableHead>
                                        <tr>
                                            <th>Prénom</th>
                                            <th>Nom</th>
                                            <th>Promo</th>
                                            <th>Dette</th>
                                        </tr>
                                    </MDBTableHead>
                                    <MDBTableBody>
                                        {value.listCompte.map((valueCompte, indexCompte) => {
                                            console.log("compte", valueCompte);
                                            return (
                                                <tr key={indexCompte}>
                                                    <td>{valueCompte.nom}</td>
                                                    <td>{valueCompte.prenom}</td>
                                                    <td>{value}</td>
                                                    <td>{valueCompte.dette}</td>
                                                </tr>
                                            )
                                        })}
                                    </MDBTableBody>
                                </MDBTable>
                            </MDBTabPane>
                        )
                    })}
                </MDBTabContent>
            </div>
        );
    else
        return (
            <MDBContainer fluid>
                <MDBRow center={true} className="mt-3">
                    <MDBCol size="6">
                        <MDBCard>
                            <MDBCardBody>
                                <img src={gifError} alt="gif-error" className="text-center img-fluid"/>
                                <h2>Erreur &#128551; : {`${this.state.errorCode}`}</h2>
                                <h4>=> {`${this.state.errorMessage}`}</h4>
                            </MDBCardBody>
                        </MDBCard>
                    </MDBCol>
                </MDBRow>
            </MDBContainer>
        );
}

}

(我已经尝试使用 map 而不是 forEach 但它不起作用)。如果有人有想法,谢谢

促销 TS 级:

interface PromoData {
    promoId: string;
    nom: string;
}

class Promo {
private _nom: string;
private _promoId: string;
private _listCompte: Array<Compte>;

constructor(proId: string, nom: string) {
    this._nom = nom;
    this._promoId = proId;
    this._listCompte = new Array<Compte>();
}

get listCompte(): Array<Compte> {
    return this._listCompte;
}

addCompte = (value: Compte) => {
    this._listCompte.push(value);
};

get promoId(): string {
    return this._promoId;
}

set promoId(value: string) {
    this._promoId = value;
}

get nom(): string {
    return this._nom;
}

set nom(value: string) {
    this._nom = value;
}

static fromData(promoData: PromoData): Promo{
    return new this(
        promoData.promoId,
        promoData.nom
    );
}

logError() : void {
    console.log(this._listCompte);
    this._listCompte.forEach((compte: Compte)=>{
        console.log(compte)
    })
}

}

和 Compte TS-Class

interface CompteData {
    promoId: string;
    nom: string;
    prenom: string;
    dette: number;
    isKefet: boolean;
    compteId: string;
}

class Compte {
private _nom: string;
private _prenom: string;
private _dette: number;
private _promoId: string;
private _isKefet: boolean;
private _compteId: string;

constructor(compteId: string, nom: string, prenom: string, dette: number, isKefet: boolean, promoId: string) {
    this._compteId = compteId;
    this._nom = nom;
    this._prenom = prenom;
    this._dette = dette;
    this._promoId = promoId;
    this._isKefet = isKefet;
}

get compteId(): string {
    return this._compteId;
}

set compteId(value: string) {
    this._compteId = value;
}

get nom(): string {
    return this._nom;
}

set nom(value: string) {
    this._nom = value;
}

get prenom(): string {
    return this._prenom;
}

set prenom(value: string) {
    this._prenom = value;
}

get dette(): number {
    return this._dette;
}

set dette(value: number) {
    this._dette = value;
}

get isKefet(): boolean {
    return this._isKefet;
}

set isKefet(value: boolean) {
    this._isKefet = value;
}

static fromData(compteData: CompteData): Compte{
    return new this(
        compteData.compteId,
        compteData.nom,
        compteData.prenom,
        compteData.dette,
        compteData.isKefet,
        compteData.promoId,
    )
}

}

(我所有的属性都有 getter 和 setter)

------- 附加测试-------

通过在渲染中添加 listPromoconsole.log

【问题讨论】:

  • 您需要在调用addCompte 的位置显示代码并使用forEach。由于您正在从数据库中获取数据,因此这可能是一个异步问题。
  • 尝试将数组 _listCompte 初始化为 [] 而不是使用类构造函数
  • 我使用 Firebase 作为 Cloud Firestore 数据库,它是 Promise “面向”的
  • @voiys 我已经尝试过了,但它什么也没解决:'(

标签: javascript reactjs typescript


【解决方案1】:

这看起来像是一个异步问题。您正在异步检索compteRefs,但您不会等到它们被检索到之后才在componentDidMount 中调用setState(),并且在最终检索到它们之后您不会调用setState()

请试试这个。这应该等到所有异步请求都完成后再调用setState()

preparePromo(doc) {
    const promoData = doc.data();

    let promo = Promo.fromData({
        nom: promoData.nom,
        promoId: promoData.promoId
    });

    return Promise.all(promoData.listCompte.map((compteRef) =>
        compteRef.get()
    )).then((comptes) => comptes
        .filter(compte => compte.exists)
        .forEach(compte => promo.addCompte(Compte.fromData(compte.data())))
    ).then(() => promo);
}

componentDidMount() {
    FirebaseAPI.getDatabase().collection("Promo")
        .onSnapshot((docSnapshot)=>{
            Promise.all(docSnapshot.map(this.preparePromo))
                .then((listPromo) => {
                    console.log("listPromo", listPromo);

                    this.setState({
                        loading: false,
                        status: true,
                        listPromo: listPromo
                    });
                });
        }, (error)=>{
            this.setState({
                loading: false,
                status: false,
                errorMessage: error.getMessage(),
                errorCode: error.getCode()
            });
        });
}

如果我有任何不匹配的括号或任何东西,请原谅。我无法对此进行测试。

另外,如果你愿意使用async/await,这段代码可以更简洁一些:

async preparePromo(doc) {
    const promoData = doc.data();

    let promo = Promo.fromData({
        nom: promoData.nom,
        promoId: promoData.promoId
    });

    const comptes = await Promise.all(promoData.listCompte.map((compteRef) =>
        compteRef.get()
    ));

    comptes
        .filter(compte => compte.exists)
        .forEach(compte => promo.addCompte(Compte.fromData(compte.data())));

    return promo;
}

componentDidMount() {
    FirebaseAPI.getDatabase().collection("Promo")
        .onSnapshot(async (docSnapshot) = >{
            const listPromo = await Promise.all(docSnapshot.map(this.preparePromo));

            console.log("listPromo", listPromo);

            this.setState({
                loading: false,
                status: true,
                listPromo: listPromo
            });
        }, (error)=>{
            this.setState({
                loading: false,
                status: false,
                errorMessage: error.getMessage(),
                errorCode: error.getCode()
            });
        });
}

【讨论】:

  • 好吧,它不起作用,但我认为这确实是个好方法。行“等待 Promise.all(docSnapshot.map(this.preparePromo));”不重新运行数组(它是未定义的返回)。也许一个想法来解决它?
  • 只需在preparePromo中的comptes.filter...之前添加return
  • @JustinMartinDev 正如 mayakwd 指出的那样,问题可能在于缺少的return。我现在已经添加了。
  • @mayakwd 谢谢。我实际上需要返回promo,因为comptes.filter... 位只有副作用。
  • @JLRishe 谢谢,它的作品。我没有意识到我可能会遇到异步问题。
猜你喜欢
  • 2018-12-06
  • 2022-07-01
  • 2019-08-09
  • 1970-01-01
  • 1970-01-01
  • 2018-09-26
  • 2018-06-16
  • 1970-01-01
相关资源
最近更新 更多