【问题标题】:Calling Set on a Firebase Database Reference Causes the Webpage to Hang在 Firebase 数据库引用上调用 Set 会导致网页挂起
【发布时间】:2018-01-09 08:23:56
【问题描述】:

我正在使用 React 和 Node 构建一个基于 Web 的界面来修改 Firebase 数据库中的数据。我之前曾在此应用中使用 Firebase Web SDK 从数据库加载数据,但在保存用户的更改时遇到了一个奇怪的问题。当我在数据库引用(即firebase.database().ref('/path/to/object').set({abc: 'xyz'}))上调用set 时,网页挂起。奇怪的是,更改保存到数据库中,但从未调用then 指定的回调(取决于浏览器,会出现This page is slowing down your browser 消息)。我确定该问题与set 有关,因为删除呼叫会消除挂起(请参阅下面我的代码中的save())。


import React from 'react'
import * as firebase from 'firebase'
// additional (unrelated) imports

export default class Editor extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            savingModal: false,
            errorModal: false,
            cancelModal: false,
            errors: []
        }
    }

    save() {
        // this.form is a Reactstrap Form
        // validate is a function that returns an array of strings
        var errors;
        // validate the form, show the errors if any
        if ((errors = this.form.validate()) && errors.length > 0)
            this.setState({errorModal: true, errors: errors})
        else {
            // collect is a function that returns an object with the data that the user entered
            var x = this.form.collect()
            // getEditorInfo is a function that returns info such as the type of object being edited
            var info = this.getEditorInfo()
            firebase.database().ref(`/${info.category}/${x.id}/`).set(x).then(() => {
                this.closeEditor()
            }, e => {
                alert(`An unexpected error occurred:\n${e}`)
            })
            this.setState({savingModal: true})
        }
    }

    // closes the window or redirects to /
    closeEditor() {
        if (window.opener) 
            window.close()
        else 
            window.location.href = '/'
    }

    render() {
        // BasicModal is a custom component that renders a Reactstrap Modal
        // IndeterminateModal is a custom component that renders a Reactstrap Modal with only a Progress element
        // EditorToolbar and EditorForm are custom components that render the UI of the page (I don't think they're relevant to the issue)
        var info = this.getEditorInfo()
        if (!info) 
            return <BasicModal isOpen={true} onPrimary={this.closeEditor} primary="Close" header="Invalid Request" body="ERROR: The request is invalid."/>
        else
            return <div>
                <EditorToolbar onSave={this.save.bind(this)} onCancel={() => this.setState({cancelModal: true})}/>
                <EditorForm ref={f => this.form = f}/>
                <BasicModal toggle={() => this.setState({cancelModal: !this.state.cancelModal})} isOpen={this.state.cancelModal} header="Unsaved Changes" body={<p>If you close this window, your changes will not be saved.<br/>Are you sure you want to continue?</p>} primary="Close Anyway" primaryColor="danger" secondary="Go Back" onPrimary={this.closeEditor}/>
                <IndeterminateModal style={{
                                top: '50%',
                                transform: 'translateY(-50%)'
                }} isOpen={this.state.savingModal} progressColor="primary" progressText="Processing..."/>
                <BasicModal toggle={() => this.setState({errorModal: false, errors: []})} isOpen={this.state.errorModal} header="Validation Error" body={<div><p>Please resolve the following errors:<br/></p><ul>{(this.state.errors || []).map(e => <li key={e}>{e}</li>)}</ul></div>} primary="Dismiss" primaryColor="primary"/>
            </div>
    }
}

【问题讨论】:

    标签: node.js reactjs firebase firebase-realtime-database


    【解决方案1】:

    2018 年 1 月 8 日更新

    我今天遇到了this article,我决定尝试一个涉及 JavaScript 的 setTimeout 方法的新解决方案。在我的情况下,在我的应用程序中调用this.setState 然后调用firebase.database().ref(path).set(data) 后发生冻结。我怀疑冻结问题是由此引起的。我猜 JavaScript 不能同时处理状态变化和 Firebase 操作。 这个新的解决方案更实用、更安全、更快、更简单。看看:

    // to perform your desired Firebase operation:
    var timeout = 50 // give JS some time (e.g. 50ms) to finish other operations first
    setTimeout(() => firebase.database().ref(path).set(data).then(
        () => {/* ... */},
        error => {/* ... */}),
    timeout)
    

    旧解决方案

    我最终找到了解决方案。我确信它可以改进,但它可以工作。我使用 Web Workers API 来运行我的 Firebase 代码。


    1. 在您的 public 文件夹 (Node.js) 中创建一个新的 JavaScript 文件
    2. 下载 Firebase Web SDK 源的副本并将其放在 public
    3. 我选择用WorkerpostMessage交流


    FirebaseWorker.js
    self.onmessage = event => {
        importScripts('./firebase.js') // import the local Firebase script
        firebase.initializeApp({/* your config */})
        const promise = p => p.then(
            () => self.postMessage({error: null}),
             e => self.postMessage({error: e})
        const doWork = () => {
            switch (event.data.action) {
                case 'get':
                    promise(firebase.database().ref(event.data.path).once('value'))
                    break;
                case 'set':
                    promise(firebase.database().ref(event.data.path).set(event.data.data))
                    break;
                case 'update':
                    promise(firebase.database().ref(event.data.path).update(event.data.data))
                    break;
                case 'remove':
                    promise(firebase.database().ref(event.data.path).remove())
                    break;
            }
        }
        if (!firebase.auth().currentUser)
            firebase.auth().signInWithEmailAndPassword(event.data.email, event.data.password).then(() => doWork())
        else
            doWork()
    }
    


    要使用 Worker:
    var worker = new Worker('FirebaseWorker.js')
    worker.onmessage = event => {
        if (event.data.error)
            alert(event.data.error)
        // ...
    }
    worker.postMessage({
        data: {/* your data (required if set or update is used) */},
        path: '/path/to/reference',
        action: 'get, set, update, or remove',
        email: 'someone@example.com',
        password: 'password123'
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-19
      • 2016-06-22
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多