我正在使用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>
}
}
更新1/8/2018
我今天遇到了这篇文章,我决定尝试一种涉及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)
旧解决方案
- 在您的
public
文件夹(Node.js)中创建一个新的JavaScript文件 - 下载Firebase Web SDK源的副本,然后将其放入
public
- 我选择与
Worker
与postMessage
进行通信
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()
}
使用工人:
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'
})