App
|
|----|---------------------|
| |
RadioInputForm TextInputForm
我正在尝试使用子组件中的数据更新父状态下的数组,但事件未触发。我已经尝试过以前的解决方案,例如{()=> callback()}和{()=> callback.bind(this)},但似乎没有任何作用。
在输入指定索引处的值后,应更新父状态下的数组。它的工作方式类似于调查Web应用程序,您应该能够返回到上一个条目,因此可以返回数组。
这是我到目前为止尝试过的,将不胜感激:
// App.js
constructor(props) {
super(props);
let radioQuestions = [], textQuestions = [];
let option = {id: "", options: {option1: false, option2: false, option3: false}, text: ""};
for(var i = 0; i < data.questions.length; ++i) {
textQuestions[i] = "";
if(data.questions[i].question_type === "RadioQuestion") {
radioQuestions[i] = option;
radioQuestions[i].id = data.questions[i].id;
} else radioQuestions[i] = {};
}
this.state = {
textQuestions: textQuestions,
radioQuestions: radioQuestions,
index: 0
};
this.handleRadioSelect= this.handleRadioSelect.bind(this);
this.handleRadioTextChange = this.handleRadioTextChange.bind(this);
this.handleTextQuestionChange= this.handleTextQuestionChange.bind(this);
this.handleBack = this.handleBack.bind(this);
this.handleNext = this.handleNext.bind(this);
}
handleRadioSelect(e) {
const { value } = e.target;
const questions = this.state.radioQuestions.slice();
const index = this.state.index;
switch(value) {
case "option1":
if(questions[index].options.option1 === true)
questions[index].options.option1 = false;
else questions[index].options.option1 = true;
break;
case "option2":
if(questions[index].options.option2 === true)
questions[index].options.option2 = false;
else questions[index].options.option2 = true;
break;
case "option3":
if(questions[index].options.option3 === true)
questions[index].options.option3 = false;
else questions[index].options.option3 = true;
break;
default:
}
this.setState({
radioQuestions: questions
});
}
handleRadioTextChange(e) {
const { value } = e.target;
const questions = this.state.radioQuestions.slice()
questions[this.state.index].text = value;
this.setState({
radioQuestions: questions
})
}
handleTextQuestionChange(e) {
const { value } = e.target;
const answers = this.state.textAnswers.slice()
answers[this.props.index] = value;
this.setState({
textAnswers: answers
})
}
具有Radio形式的子组件:
constructor(props) {
super(props);
this.state = {
radioQuestions: this.props.radioQuestions
}
this.handleRadioTextChange = this.handleRadioTextChange.bind(this);
this.handleRadioSelect = this.handleRadioSelect.bind(this);
}
handleRadioSelect(e) {
const { value } = e.target;
const questions = this.state.radioQuestions.slice();
const index = this.state.index;
console.log(JSON.stringify(questions[index]));
switch(value) {
case "option1":
if(questions[index].options.option1 === true)
questions[index].options.option1 = false;
else questions[index].options.option1 = true;
break;
case "option2":
if(questions[index].options.option2 === true)
questions[index].options.option2 = false;
else questions[index].options.option2 = true;
break;
case "option3":
if(questions[index].options.option3 === true)
questions[index].options.option3 = false;
else questions[index].options.option3 = true;
break;
default:
}
this.props.handleRadioSelect(e);
this.setState({
radioQuestions: questions
});
}
handleRadioTextChange(e) {
const { value } = e.target;
const questions = this.state.radioQuestions.slice()
questions[this.props.index].text = value;
this.setState({
radioQuestions: questions
})
}
render() {
return (
<div className="form-group">
<input type="radio" name="option1"
value="option1"
onChange = { this.state.handleRadioSelect }
defaultChecked={ this.state.radioQuestions[this.props.index].options.option1 } /> Option 1<br />
<input type="radio" name="option2"
value="option2"
onChange = { this.state.handleRadioSelect }
defaultChecked={ this.state.radioQuestions[this.props.index].options.option2 } /> Option 2<br />
<input type="radio" name="option3"
value="option3"
onChange = { this.state.handleRadioSelect }
defaultChecked={ this.state.radioQuestions[this.props.index].options.option3 } /> Option 3<br />
<textarea rows="1" cols="40" id="answer" name="answer"
className ="form-control input-default"
onChange={ this.state.handleRadioTextChange }
defaultValue = { this.state.radioQuestions[this.props.index].text } />
</div>
);
}
重复的代码(在父级和子级中)是我为解决此问题而进行的尝试。最后是“文本输入”表单组件:
constructor(props) {
super(props);
this.state = {
textAnswers: this.props.textQuestions,
};
this.handleTextQuestionChange = this.handleTextQuestionChange.bind(this);
}
handleTextQuestionChange(e) {
const { value } = e.target;
const answers = this.state.textAnswers.slice()
answers[this.props.index] = value;
this.props.handleTextQuestionChange(e);
this.setState({
textAnswers: answers
})
}
render() {
return (
<div className="form-group">
<textarea rows="4" cols="50" id="answer" name="answer"
className ="form-control input-default" onChange = { this.state.handleTextQuestionChange }
defaultValue = { this.state.textAnswers[this.props.index] } />
</div>
);
}
如果我不遵循良好做法,也将感谢您提供任何其他建议,谢谢。
答案 0 :(得分:2)
简短答案和有效的代码和框:https://codesandbox.io/s/21q3j32qyy 为了了解问题出在哪里,我重构了很多东西并删除了重复的代码。
下面有很长的答案...
我们有一个对象,该对象应该来自API或包含用户问题列表以及一个或一个或多个其他信息(如调查标题)的对象。
现在,可以在App.js
中通过以下方式检索示例数据:
var data = require("./payload.json");
合法的,即使使用ES6,也可以这样写(为了与代码的其余部分保持一致):
import data from "./payload.json"
通过查看payload.json
文件,可以看到存在3种可能的问题类型:
问题还包含要提示用户的文本,ID和区分它们的类型。
用户还应该能够浏览他/她的答案。
在我看来,您基本上...需要使用此表格来保持用户的所有状态(我想您应该将它们发送到API一段时间)。
这是应用程序或至少是示例的主要组件。
在这里我们应该:
在构造函数中,您所做的事情对我来说似乎很奇怪(当然,如果出于某种原因它是正确的,请告诉我):您遍历数据变量中的每个问题并创建两个单独的数组,其中一个用于文本问题一个用于广播问题,当问题属于另一种类型时,保留两个假元素...
相反,我想创建一个空答案的唯一数组,每种问题的字段都不同。因此,我创建了一个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
文件中建立实用程序,我创建了一个方法,该方法从问题开始返回“ answer template”普通对象。为了获得结果,我只需要检查问题类型,然后将一些常见字段与问题类型的专用字段合并即可:
// 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;
}
};
在这里,我决定处理未知类型的问题:在开关的默认情况下返回null将过滤掉这些问题。
同样,我编写了一个超级小型实用程序方法来执行此检查:
// Answers.js
const isValid = answer => answer !== null;
最后,一个方法将所有问题映射到所有(有效)“答案模板”,然后将其像普通对象一样导出(只是因为我喜欢我们将在{{1}中获得的语法) }组件(使用此方法时):
App.js
这时,我们可以在App.js中导入Answers.js并通过以下方式设置状态变量:
// Answers.js
const from = questions =>
(questions || []).map(fromQuestion).filter(isValid);
export default {
from
};
我们将在// 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: ""
}, {
...
您正在执行一种方法,该方法生成一些要呈现的内容……只不过是组件!
因此,为了以React的方式进行操作,在// 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">
...
文件中,我定义了一个类似于您的Question组件:
Question.js
我分解了prop类型,并渲染了一个与其他props相同的组件。
继续进行编辑(恐怕会丢失所有内容!)
答案 1 :(得分:0)
我认为这里的主要问题是您的状态是持有一个包含对象的数组,这些对象无法使用Array.slice
函数进行克隆。请注意,此函数只是对数组进行浅表复制,以使其中的对象在状态转换之间保持不变。
我建议使用ImmutableJS处理深层物体的状态突变。