#reactjs #firebase #loops #google-cloud-firestore
#reactjs #firebase #циклы #google-облако-firestore
Вопрос:
Я пытаюсь выполнить цикл в ReactJS.
У меня есть база данных, которая имеет следующую структуру:
Курсы (коллекция) -> тестовый курс (документ) -> вопросы (коллекция)
В коллекции вопросов есть документ для каждого вопроса, в котором есть поле с указанием типа вопроса.
Я хочу, чтобы мой код перебирал каждый вопрос, находил тип вопроса, а затем выводил компонент в зависимости от того, чему он соответствует.
В настоящее время он выводит последний документ и компонент в зависимости от типа, но я не совсем уверен, как я буду это делать.
код
import './App.css';
import firebaseConfig from './firebase/index'
import { Component } from 'react';
import TrueOrFalse from './components/TrueOrFalse/TrueOrFalse'
import firebase from 'firebase/app'
import MultipleChoice from './components/MultipleChoice/MultipleChoice'
class App extends Component {
state = {
questionType: null
}
componentDidMount() {
let db = firebase.firestore()
db.collection('courses').doc('testcourse1').collection('questions').get().then(snapshot =>{
const questionTypes = []
snapshot.docs.forEach(doc => {
const data = doc.data()
this.setState (data)
questionTypes.push(data)
console.log(this.state)
})
})
}
render(){
let question = null;
if (this.state.questionType === "TrueOrFalse")
question = <TrueOrFalse/>
else if (this.state.questionType === "MultipleChoice")
question = <MultipleChoice/>
return (
<div>
{question}
</div>
)
}
}
export default App
Ответ №1:
Причина, по которой он показывает только последний, заключается в том, что вы вызываете this.setState(data) на каждой итерации цикла. Поэтому он переопределяет каждое предыдущее значение. Вместо этого, если вы хотите, чтобы все типы вопросов были в состоянии, вам нужно сохранить их все в состоянии. Удалите текущий вызов setState (data) и поместите новый, подобный этому,
snapshot.docs.forEach(doc => {
...
})
this.setState(questionTypes);
Это сохранит их все в состоянии компонента. Далее вам нужно выполнить итерацию по this.state .questionTypes (я переименовал questionType в questionTypes). Например, вы могли бы сделать это,
render() {
return (
<div>
{this.state.questionTypes.map(questionType => {
if (questionType === 'TrueOfFalse') {
return <TrueOfFalse />
} else if (questionType === 'MultipleChoice') {
return <MultipleChoice />
}
// you're missing some cases, so I'm just returning null
return null;
}}
</div>
)
}
Комментарии:
1. Спасибо за это! Это именно то, что я себе представлял, однако теперь он возвращает ошибку «TypeError: не удается прочитать свойство ‘map’ null»
2. Нет проблем. Скорее всего, это означает, что this.state. Значение questionTypes не определено или равно null. Убедитесь, что вы присвоили ему начальное значение в состоянии (пустой массив). Затем также убедитесь, что вы всегда присваиваете ему значение массива (никогда null). Кроме того, убедитесь, что вы изменили имя на questionTypes (с s) вместо questionType .
Ответ №2:
Я очень мало понимаю вашу структуру данных, однако приведенный ниже переработанный код может помочь вам продвинуться к разрешению. Я сделал несколько предположений. Дайте мне знать, если вам понадобится дополнительная помощь по этому вопросу. Приветствия!
import React, { Component } from 'react';
import './App.css';
import firebase from 'firebase/app';
// Comment out for testing purposes - see in code replacement below
// import TrueOrFalse from './components/TrueOrFalse/TrueOrFalse';
// import MultipleChoice from './components/MultipleChoice/MultipleChoice';
// Contants for conditional component rendering
const TRUE_OR_FALSE = 'TrueOrFalse';
const MULTIPLE_CHOICE = 'MultipleChoice';
// Assumed returned data from firebase to replace firebase actual data for testing purposes
const dataFromFirebase = [{
questionType: 'TrueOrFalse',
question: 'Are you alive?',
responses: [{
id: 1,
response: 'Yes'
}, {
id: 2,
response: 'No'
}]
}, {
questionType: 'MultipleChoice',
question: 'What languages do you like?',
responses: [{
id: 1,
response: 'Javascript'
}, {
id: 2,
response: 'Python'
}, {
id: 3,
response: 'Java'
}]
}];
// Minimalistic assumption of your <TrueOrFalse /> component to replace the imported component of same name for testing purposes
const TrueOrFalse = ({ componentData }) => {
return (
<div>
<ul>
{
componentData.map(data => (
<li key={data.id}>
<label htmlFor={data.id}>{data.response}</label>
<input type="radio" id={data.id} name={data.response} value={data.id}></input>
</li>
))}
</ul>
</div>
)
}
// Minimalistic assumption of your <MultiChoice /> component to replace the imported component of same name for testing purposs
const MultipleChoice = ({ componentData }) => {
return (
<div>
<ul>
{
componentData.map(data => (
<li key={data.id}>
<input type="checkbox" id={data.id} name={data.response} value={data.id}>{data.response}</input>
</li>
))}
</ul>
</div>
)
}
class App extends Component {
constructor() {
this.state = {
questionDocuments: []
};
}
componentDidMount() {
let db = firebase.firestore();
db.collection('courses')
.doc('testcourse1')
.collection('questions')
.get()
.then(snapshot => {
snapshot.docs.forEach(doc => {
const data = doc.data();
console.log(data); // Data from firebase
this.setState({
questionDocuments: dataFromFirebase // mock assumbed data from firebase for testing purposes
});
});
});
}
render() {
const { questionDocuments } = this.state;
return (
questionDocuments.map(document => {
if (document.questionType === TRUE_OR_FALSE) {
return (
<div>
<div>{document.question}</div>
<TrueOrFalse componentData={document.responses} />
</div>
);
}
if (document.questionType === MULTIPLE_CHOICE) {
return (
<div>
<div>{document.question}</div>
<MultipleChoice componentData={document.responses} />
</div>
);
}
})
);
}
}
export default App;