Краткий ответ и рабочие коды и коробка: https://codesandbox.io/s/21q3j32qyy
Чтобы понять, в чем проблема, я много чего переработал и удалил дублированный код.
Длинный ответ ниже ...
Сначала краткий обзор проблемы
У нас есть объект, который должен исходить из API или любого другого объекта, который содержит список вопросов для пользователя и одну или дополнительную информацию, например заголовок опроса.
Прямо сейчас образцы данных извлекаются в App.js
с помощью:
var data = require("./payload.json");
Законно, даже если, используя ES6, его можно записать (для соответствия остальной части кода) следующим образом:
import data from "./payload.json"
Если посмотреть в файл payload.json
, можно ли увидеть, что есть 3 возможных типа вопросов:
- с 3-мя опциями, которые ведут себя как флажки (даже если в вашем коде они радио) и текст;
- только с текстом;
- с загрузчиком изображений;
Вопрос также содержит текст, который должен быть предложен пользователю, идентификатор и тип для их различения.
Пользователь также должен иметь возможность перемещаться по его / ее ответам.
Цель
Мне показалось, что вам в основном ... нужна эта форма для работы и сохранения всех ответов пользователя в состоянии (я полагаю, что вы должны отправить их в API когда-нибудь).
App.js
Это основной компонент приложения или, по крайней мере, образца.
Здесь мы должны:
- принимать / обрабатывать вопросы, полученные откуда-то;
- сделать текущий вопрос;
- возможность перемещаться по вопросам;
- обрабатывать ответы пользователей;
В своем конструкторе вы делаете что-то странное для меня (конечно, если вместо этого оно по какой-то причине корректно, дайте мне знать): вы циклически просматриваете каждый вопрос в переменной данных и создаете два отдельных массива, один для текстовых вопросов. и один для радио вопросов, сохраняя в обоих поддельных элементах, когда вопрос другого типа ...
Вместо этого я подумал создать уникальный массив пустых ответов с разными полями для каждого типа вопроса. Поэтому я создал файл Answers.js
, в котором можно обрабатывать несколько вещей.
Во-первых, у меня есть константы, содержащие поля (и их значения по умолчанию) для различных типов вопросов:
// Answers.js
const RADIO_FIELDS = {
options: {
option1: false,
option2: false,
option3: false
}
};
const TEXT_FIELDS = {
text: ""
};
const UPLOAD_FIELDS = {
file: "",
imageUrl: ""
};
Я также создал другой файл Question.js
, в котором я определяю некоторые служебные константы для работы с типами вопросов, определенными в ваших данных:
// Question.js
export const RADIO_QUESTION_TYPE = "RadioQuestion";
export const TEXT_QUESTION_TYPE = "TextQuestion";
export const UPLOAD_QUESTION_TYPE = "FileUpload";
export const QuestionTypes = {
RADIO: RADIO_QUESTION_TYPE,
TEXT: TEXT_QUESTION_TYPE,
UPLOAD: UPLOAD_QUESTION_TYPE
};
Установлены утилиты, в файле Answers.js
я создал метод, который возвращает простой объект «шаблон ответа», начиная с вопроса. Чтобы получить результат, я просто проверяю тип вопроса и объединяю некоторые общие поля со специальными полями типа вопроса:
// Answers.js
const fromQuestion = question => {
const baseAnswer = {
id: question.id,
type: question.question_type
};
switch (question.question_type) {
case QuestionTypes.RADIO:
return Object.assign({}, baseAnswer, RADIO_FIELDS, TEXT_FIELDS);
case QuestionTypes.TEXT:
return Object.assign({}, baseAnswer, TEXT_FIELDS);
case QuestionTypes.UPLOAD:
return Object.assign({}, baseAnswer, UPLOAD_FIELDS);
default:
return null;
}
};
Здесь я принял решение об обработке вопросов неизвестного типа: возвращение нулевого значения в случае переключателя по умолчанию отфильтрует эти вопросы от отображения.
Опять же, я написал очень маленький служебный метод, который выполняет эту проверку:
// Answers.js
const isValid = answer => answer !== null;
Наконец, метод сопоставляет все вопросы со всеми (действительными) «шаблонами ответов», а затем экспортируется как простой объект (просто потому, что мне нравится синтаксис, который мы получим в App.js
компоненте когда мы будем использовать этот метод):
// Answers.js
const from = questions =>
(questions || []).map(fromQuestion).filter(isValid);
export default {
from
};
На данный момент мы можем импортировать Answers.js в App.js и установить переменную состояния следующим образом:
// App.js
...
import Answers from './Answers';
export default class App extends React.Component {
state = {
answers: Answers.from(data.questions),
index: 0
};
...
У нас будет этот объект внутри свойства answers
:
[
{
id: 2501,
type: "RadioQuestion",
options: {
option1: false,
option2: false,
option3: false
},
text: ""
}, {
id: 2447,
type: "TextQuestion",
text: ""
}, {
...
Хорошо, у нас есть структура данных и простой способ получить ее из необработанных данных.
Теперь мы должны правильно задать вопрос. В вашем коде бывает так:
// App.js, render method
...
render() {
const question = () => {
switch (data.questions[this.state.index].question_type) {
case "RadioQuestion":
return (
<RadioQuestion
radioQuestion={this.state.radioQuestions[this.state.index]}
handleRadio={this.handleRadio}
/>
);
case "TextQuestion":
return (
<TextQuestion
textQuestion={this.state.textQuestions[this.state.index]}
handleText={this.handleText}
/>
);
case "FileUpload":
return <FileUpload index={this.state.index} />;
default:
return <h2>Unknown Question Format</h2>;
}
};
return (
...
</label>
{ question() }
<div className="form-group input-margin-top">
...
Вы выполняете метод, который генерирует некоторый контент для рендеринга ... это не более чем компонент!
Итак, чтобы сделать это способом React, в файле Question.js
я определил компонент Question, похожий на ваш:
// Question.js
const Question = ({ type, ...props }) => {
switch (type) {
case RADIO_QUESTION_TYPE:
return <RadioQuestion {...props} />;
case TEXT_QUESTION_TYPE:
return <TextQuestion {...props} />;
case UPLOAD_QUESTION_TYPE:
return <FileUpload {...props} />;
default:
return <h1>Unknown Question Format</h1>;
}
};
export default Question;
Я деструктурирую тип prop и создаю другой компонент, просто передавая все остальные реквизиты.
ПРОДОЛЖЕНИЕ ПРОДОЛЖИТЬ РЕДАКТИРОВАТЬ (я боюсь все потерять!)