javascript - 反应表单 - 值更改时自动取消选择字段
问题描述
我是 React 的初学者,正在尝试构建我的第一个表单。
我使用 API 获取在所述表单中提出的问题,一旦提交表单,我想通过相同的 API 创建一个答案对象。
问题是一切似乎都很好(问题呈现正常,答案对象在状态下更新),但是每次字段的值发生变化时,我都必须重新选择该字段才能继续输入。基本上,就像我在值更改时自动点击离开该字段一样。
这是发生的事情:
这是(可能)有问题的代码片段:
class Form extends React.Component {
constructor(props) {
super(props);
{/* Questions are filled on page load, answers is what I'm working with */}
this.state = { questions: [], answers: [] };
this.handleChange = this.handleChange.bind(this);
}
// This is where the magic is supposed to happen
handleChange(event) {
let key = event.target.id,
value = event.target.value;
{/* Here, my goal is to build an object with questions ids as keys and values of fields as key values. */}
this.setState(prevState => {
let copy = Object.assign({}, prevState.answers);
copy[key] = value;
return { answers: copy };
})
}
render() {
const { questions } = this.state;
const TextareaInput = (fieldId) => (
<div>
<textarea name={ fieldId.fieldId } value={this.state.answers[fieldId.fieldId]} id={ fieldId.fieldId } onChange={this.handleChange} ></textarea>
</div>
);
const TextInput = (fieldId) =>(
<div>
<input type='text' name={ fieldId.fieldId } value={this.state.answers[fieldId.fieldId]} id={ fieldId.fieldId } onChange={this.handleChange} />
</div>
);
const allQuestions = (
questions.map((q, key) =>
<div key={q.id} className='question'>
<label htmlFor={ q.field_id } className={ q.required ? 'required' : '' } dangerouslySetInnerHTML={{__html: q.question}}></label>
{q.field_type == 'text' ? <TextInput fieldId={q.field_id}/> : <TextareaInput fieldId={q.field_id}/>}
</div>
)
)
return (
<form>
{ allQuestions }
</form>
)
}
}
export default Form;
我认为问题来自我的 handleChange 函数,但我不确定是什么原因造成的。我尝试添加一些东西并在没有任何运气的情况下移动一些东西......
解决方案
您需要调用TextInput
和TextareaInput
类似的函数,而不是像单独的组件一样使用它们,因为您在组件中定义了它们。
{q.field_type == 'text'
? TextInput(q.field_id)
: TextareaInput(q.field_id)}
React 无法保持对它们的直接引用,并且似乎在每次渲染时都将它们视为不同的元素。
另外,我相信您已经知道,dangerouslySetInnerHTML
顾名思义,您应该小心使用,这可能很危险。
class Form extends React.Component {
constructor(props) {
super(props);
{
/* Questions are filled on page load, answers is what I'm working with */
}
this.state = {
questions: [
{
id: 2,
field_id: 2,
question: 'How are you today?',
field_type: 'text',
},
{
id: 3,
field_id: 3,
question: 'What\'s the answer to life, the universe, and everything??',
field_type: 'textarea',
},
],
answers: [],
};
this.handleChange = this.handleChange.bind(this);
}
// This is where the magic is supposed to happen
handleChange(event) {
let key = event.target.id,
value = event.target.value;
{
/* Here, my goal is to build an object with questions ids as keys and values of fields as key values. */
}
this.setState((prevState) => {
let copy = Object.assign({}, prevState.answers);
copy[key] = value;
return { answers: copy };
},()=>{console.log(this.state)});
}
render() {
const { questions } = this.state;
const TextareaInput = (fieldId) => (
<div>
<textarea
name={fieldId}
value={this.state.answers[fieldId]}
id={fieldId}
onChange={this.handleChange}
></textarea>
</div>
);
const TextInput = (fieldId) => (
<div>
<input
type="text"
name={fieldId}
value={this.state.answers[fieldId]}
id={fieldId}
onChange={this.handleChange}
/>
</div>
);
const allQuestions = questions.map((q, key) => (
<div key={q.id} className="question">
<label
htmlFor={q.field_id}
className={q.required ? 'required' : ''}
// As I'm sure you are already aware, this is likely a terrible idea.
dangerouslySetInnerHTML={{ __html: q.question }}
></label>
{q.field_type == 'text'
? TextInput(q.field_id)
: TextareaInput(q.field_id)}
</div>
));
return <form>{allQuestions}</form>;
}
}
ReactDOM.render(<Form/>, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
为了避免使用dangerouslySetInnerHTML
问题,我建议使用某种降价渲染器。对于大多数提问的用例来说,这应该足够好了。
https://www.npmjs.com/package/react-markdown
https://www.markdownguide.org/
class Form extends React.Component {
constructor(props) {
super(props);
{
/* Questions are filled on page load, answers is what I'm working with */
}
this.state = {
questions: [
{
id: 2,
field_id: 2,
question: 'How are *you* today?',
field_type: 'text',
},
{
id: 3,
field_id: 3,
question: 'What\'s the **answer** to life, the universe, and everything??',
field_type: 'textarea',
},
{id: 4,
field_id: 4,
field_type: 'text',
question:`# This is the big question
#### *ARE YOU READY?*
1. Is this the real life?
1. Or is this just fantasy?
`
}
],
answers: [],
};
this.handleChange = this.handleChange.bind(this);
}
// This is where the magic is supposed to happen
handleChange(event) {
let key = event.target.id,
value = event.target.value;
{
/* Here, my goal is to build an object with questions ids as keys and values of fields as key values. */
}
this.setState((prevState) => {
let copy = Object.assign({}, prevState.answers);
copy[key] = value;
return { answers: copy };
},()=>{console.log(this.state)});
}
render() {
const { questions } = this.state;
const TextareaInput = (fieldId) => (
<div>
<textarea
name={fieldId}
value={this.state.answers[fieldId]}
id={fieldId}
onChange={this.handleChange}
></textarea>
</div>
);
const TextInput = (fieldId) => (
<div>
<input
type="text"
name={fieldId}
value={this.state.answers[fieldId]}
id={fieldId}
onChange={this.handleChange}
/>
</div>
);
const allQuestions = questions.map((q, key) => (
<div key={q.id} className="question">
<label
htmlFor={q.field_id}
className={q.required ? 'required' : ''}
><ReactMarkdown source={q.question}/></label>
{q.field_type == 'text'
? TextInput(q.field_id)
: TextareaInput(q.field_id)}
</div>
));
return <form>{allQuestions}</form>;
}
}
ReactDOM.render(<Form/>, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-markdown/4.3.1/react-markdown.js" integrity="sha256-4jDgUokdWbazrdnMjWm+TftvBFnOwSNIpvKhgYsInfw=" crossorigin="anonymous"></script>
<div id="root" />
如果您需要完整的渲染功能:
https://pragmaticwebsecurity.com/files/cheatsheets/reactxss.pdf给出了一个使用DOMPurify来清理输入以防止跨站点脚本和其他在 html 之外呈现的危险行为的示例。
因此,为此,您将净化字符串,然后在净化后将其传递给它dangerouslySetInnerHTML
。
推荐阅读
- android - 带有 JSON 的 AutoCompleteTextView
- selenium - 按回车键,然后在单行或条件上输入文本
- excel - 如何使用 VBA 将特定数据从一个工作表复制到另一个工作表
- javascript - How can I make my modal box faded behind pop-up box?
- xml - XML Parsing Error: not well-formed FOR product_id=1100
- python - How to test postgres statement_timeout in django
- javascript - 如何修复 vuex 中的“未定义”值?
- spring - 是否可以通过证书仅保护一个弹簧引导休息端点?
- c# - 一种在代码中的标记之间获取字符串的方法
- java - 如何序列化 Runnable 以在另一个 infinispan 节点上执行代码