问题
Slight issue here which I think is relatively simple to solve but I can't quite get my head around. I'm quite new to React. I've decided to make a small sample app which just takes the input from two fields, saves them to Firebase and outputs those values on the page. It works completely fine in terms of submitting data and retrieving it, but when I click the submit button to add the data to Firebase it seems to duplicate the data stored in the state and render them twice:
Parent Component:
import React, { Component, Fragment } from 'react';
import firebase from '../../config/firebase';
import QuestFormField from './QuestFormField/QuestFormField';
import QuestFormSelection from './QuestFormSelection/QuestFormSelection';
import classes from './QuestForm.css';
class QuestForm extends Component {
state = {
value: '',
points: 0,
items: []
}
questHandler = e => {
this.setState({
value: e.target.value,
});
}
pointsHandler = e => {
this.setState({
points: e.target.value,
});
}
submitHandler = e => {
e.preventDefault();
const itemsRef = firebase.database().ref('quest');
const items = {
quest: this.state.value,
points: this.state.points
}
itemsRef.push(items);
this.setState({
value: '',
points: 0
});
}
render () {
return (
<Fragment>
<form className={classes.Form} onSubmit={this.submitHandler}>
<QuestFormField val='Quest' inputType='text' name='quest' value={this.state.value} changed={this.questHandler} />
<QuestFormField val='Points' inputType='number' name='points' value={this.state.points} changed={this.pointsHandler} />
<button>Away! To Firebase!</button>
</form>
<QuestFormSelection />
</Fragment>
);
}
}
export default QuestForm;
Child Component (Form Fields)
import React from 'react';
import classes from './QuestFormField.css';
const QuestFormField = (props) => (
<div className={classes.Container}>
<label htmlFor={props.name}>{props.val}</label>
<input type={props.inputType} name={props.name} onChange={props.changed}/>
</div>
);
export default QuestFormField;
Child Component B (Data Retriever/Displayer)
import React, { Component, Fragment } from 'react';
import firebase from '../../../config/firebase';
import classes from './QuestFormSelection.css';
class QuestFormSelection extends Component {
state = {
quests: []
}
componentDidMount() {
const database = firebase.database();
const quests = [];
database.ref('quest').on('value', (snapshot) => {
snapshot.forEach((childSnapshot) => {
quests.push({
id: childSnapshot.key,
quest: childSnapshot.val().quest,
points: childSnapshot.val().points,
});
});
console.log(quests);
this.setState(() => {
return {
quests: quests
}
});
console.log(this.state.quests);
});
}
render () {
return (
<section className='display-item'>
<div className="wrapper">
{this.state.quests.map(quest => (
<div key={quest.key}>
<p>{quest.quest}</p>
<p>{quest.points}</p>
</div>
))}
</div>
</section>
)
}
}
export default QuestFormSelection;
Example of behaviour here:
https://i.gyazo.com/c70972f8b260838b1673d360d1bec9cc.mp4
Any pointers would help :)
回答1:
I haven't used firebase myself, but it looks like the code below is setting up a listener to "quest" changes which will execute each time a change occurs, but you defined const quests = []
outside of the db change handler. This means that on the second change, you will push everything in the snapshot to the same quests
array that may have already had previous snapshots added to it. I believe you can fix this by moving the quests
variable inside the listener function as shown below.
componentDidMount() {
const database = firebase.database();
database.ref('quest').on('value', (snapshot) => {
const quests = [];
snapshot.forEach((childSnapshot) => {
quests.push({
id: childSnapshot.key,
quest: childSnapshot.val().quest,
points: childSnapshot.val().points,
});
});
console.log(quests);
this.setState(() => {
return {
quests: quests
}
});
console.log(this.state.quests);
});
}
来源:https://stackoverflow.com/questions/54206722/react-state-storing-outputting-duplicate-values