Зацикливание компонентов в ReactJS

#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;