我正在制作一个允许用户进行测验的表格(到目前为止,这是我的代码)
var uuid = require("uuid-v4");
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true
var questionNum = 0;
class App extends Component {
constructor(props) {
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
};
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
}
componentDidMount() {
// componentDidMount() is a React lifecycle method
this.addQuestion();
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
/**
* It's probably better to structure your questions like this:
* this.state.questions: [{
* question: 'How are you?',
* answers: ['Good', 'Great', 'Awful'],
* // correctAnswer: 'Great'
* },
* {
* question: 'What is your name?',
* answers: ['Toby', 'Marco', 'Jeff'],
* // correctAnswer: 'Jeff'
* }];
*
* This allows you to keep better track of what questions
* have what answers. If you're making a 'quiz' type structure,
* you could additionally add a `correctAnswer` property.
*/
addQuestion() {
questionNum++;
this.setState(previousState => {
const questions = [...previousState.questions, "question", "hi"];
const answers = [...previousState.answers];
for (var i = 0; i < 4; i++) {
answers.push({
answerChoice: "",
key: uuid()
});
}
return { questions, answers };
});
console.log(
this.state.answers,
this.state.questions,
questionNum,
this.state.title,
this.state.author
);
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title:{" "}
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
name="title"
/>
<br />
Who's the Author?{" "}
<input
type="text"
value={this.state.author}
onChange={this.handleChange}
name="author"
/>
<br />
<br />
</div>
<div className="questions">
Now let's add some questions... <br />
{// This is where we loop through our questions to
// add them to the DOM.
this.state.questions.map(question => {
return <div>{question}</div>;
})
// This is what it would look like for the structure
// I proposed earlier.
// this.state.questions.map((question) {
// return (
// <div>{question.quesion}</div>
// {
// question.answers.map((answer) => {
// return (<div>{answer}</div>);
// })
// }
// );
// })
// This would output all questions and answers.
}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;
现在,我想尝试并能够“删除”一个问题(使用按钮)。我的代码现在要做的是将对象添加到数组中,而我已经弄清楚了。但是现在我正在尝试从数组中删除项目。我当时在想“可以,只删除最后一个问题”,但实际上,用户会希望删除他们的任何问题。我只是好奇是否有人对此有一些建议,我真的不知道如何开始。
答案 0 :(得分:7)
如果您希望用户能够删除任何问题,请将onClick
添加到问题div
(或div的子级-请记住移动onClick
)。为此的回调可以接受索引,该索引引用列表中要删除的元素。
示例:
class App extends Component {
constructor(props) {
super(props)
this.removeItem = this.removeItem.bind(this)
}
removeItem (index) {
this.setState(({ questions }) => {
const mQuestions = [ ...questions ]
mQuestions.splice(index, 1)
return { questions: mQuestions }
})
}
render () {
return (
<div>
...
{ this.state.questions.map((question, index) => {
return <div onClick={ () => this.removeItem(index) }>{ question }</div>
}) }
</div>
)
}
}
答案 1 :(得分:1)
这不是一个JavaScript问题,而是一个反应性问题。由于您的问题以反应状态存储,因此修改状态后,它将更新DOM。只需使用this.setState()
从数组中删除值即可。
您有一些选择可以从数组中删除值。这里要记住的主要事情是确保您不修改实际的数组,而是将其替换为新的数组实例。直接修改数组不会触发更新,并且违反了反应状态的一般原则。例如,使用
Array.prototype.splice()
将修改您的原始数组。
(Array.prototype.splice docs)
在JavaScript中,诸如字符串和数字之类的基元是通过值传递的,而诸如数组,集合或通用JavaScript对象之类的对象则是通过引用传递的。这意味着通过将对象分配给新变量,您现在将拥有两个指向同一对象的变量。
const foo = [1];
const bar = foo;
console.log(foo); // [1]
console.log(bar); // [1]
foo.push(2);
console.log(foo); // [1, 2]
console.log(bar); // [1, 2]
解决此问题的一种常见方法是使用ES6扩展符号(Spread operator docs)将值扩展到新数组中。 const bar = [...foo]
将返回指向一个单独对象的复制数组。将其应用于您的问题,您可以执行const q = [...this.state.questions]
,然后使用q.splice(index, 1)
修改q,然后使用this.setState()
将其分配给您的状态。很显然,还有其他选项可以从数组中删除项目,但我想您知道数组的索引并不是已知的。在这种情况下,诸如Array.prototype.find()
或Array.prototype.findIndex()
之类的工具会很有帮助,或者您可以使用JavaScript Map对象(Map docs)代替数组来消除对索引的需求,同时保持问题的顺序。所有这些选项都同样有效,因此我将由您决定要怎么做。
要真正触发删除,您需要在页面上具有某种用户控制。您可以通过单击元素上的侦听器,HTML中包含的每个问题的特定按钮,或者甚至删除其他位置的下拉菜单来执行此操作。最终结果将是,与用户交互的元素将保持唯一的ID,以便在激活回调函数时可以确定应该删除哪个问题。此ID将存储在函数的第一个参数中。在大多数示例中,这将被命名为“事件”。