【发布时间】:2020-06-06 01:42:43
【问题描述】:
在 React 中,我创建了一个组件来允许用户提交一本书以添加到书籍列表中。该站点在视觉上看起来像这样(AddBook 组件用红色框起来):
我将在下面进一步分享我的完整代码,但首先我只需要解释问题。在AddBook 组件中,“选择作者”下拉菜单(您在上面的屏幕截图中看到)使用<select> <option> 元素,它允许用户在提交表单以添加书籍之前从作者列表中进行选择.作者列表是从 GraphQL API 调用中获取的。
sn-p 代码:
function displayAuthors() {
if (loading) return (<option disabled>'Loading authors...'</option>);
if (error) return (`Error: ${error.message}`);
return data.authors.map((author) => {
return (
<option key={author.id} value={author.id}>{author.name}</option>
);
});
}
return (
<form id="add-book" onSubmit={submitBook}>
<h1>Add a new book to Wiki Books</h1>
<div className="field">
<label>Book name: </label>
<input type="text" onChange={handleChange} name="name" required value={bookEntry.name}/>
</div>
<div className="field">
<label>Genre: </label>
<input type="text" onChange={handleChange} name="genre" required value={bookEntry.genre}/>
</div>
<div className="field">
<label>Author (select author): </label>
<select onChange={handleChange} name="authorId">
{displayAuthors()}
</select>
</div>
<button type="submit">+</button>
</form>
我遇到的障碍是确保下拉菜单 <select> 元素是必需的,因此它确保在提交图书信息之前需要选择作者。现在,通过论坛搜索后,我得出结论,React 似乎没有为<select> REQUIRED 验证实现本机解决方案。取而代之的是复杂的黑客解决方案作为解决方法。
为了缓解这个问题,我只是删除了第一个 <option> Select author </option>(您在共享的代码中看不到它,因为我已经删除了它)。因此,默认情况下,下拉菜单设置在列表中的第一作者,在页面的初始呈现上。从而强制用户选择默认选择的作者。当然,他们总是可以更改选项以选择不同的作者。但关键是默认情况下已经强制执行作者选择。
现在我遇到这种方法的下一个问题是 - 在页面的初始呈现时,即使默认情况下已经在下拉列表中选择了作者,该作者的相应 <option> 元素,其值为 @987654333 @ 在组件的初始渲染时没有被 React 检测到(来自 option 元素的这个 authorId 值是图书提交所必需的,调用 API 将图书信息提交到数据库)。
您必须首先更改<select> 元素中onChange 属性事件侦听器的菜单选项,以检测所选<option>(authorId)的值。这意味着我确保始终选择作者(即使在初始页面呈现时)的解决方案现在毫无意义,因为 React 不会获取初始选定作者的 authorID 值。
<option key={author.id} value={author.id}>{author.name}</option> // value={author.id} doesn't get detected in initial render of page, unless menu option is changed for `onChange` to detect value={authorId}
.
为了解决这个问题,我的解决方案是创建一个状态,该状态将设置为从 API 调用获取的作者列表中的第一作者。所以应该是Patrick Rothfuss - 下拉菜单中默认选择的作者。所以Patrick Rothfuss的authorId设置为这个状态。如果用户在提交表单时根本没有更改下拉菜单,那么这个状态可以用于书籍提交。
const [firstAuthorId, setFirstAuthorId] = useState("");
function displayAuthors() {
if (loading) return (<option disabled>'Loading authors...'</option>);
if (error) return (`Error: ${error.message}`);
return data.authors.map((author, index) => {
if (index === 0) {
setFirstAuthorId(author.id);
}
return (
<option key={author.id} value={author.id}>{author.name}</option>
);
});
}
现在我面临的问题是,当我将状态设置为第一作者 ID 时,出现以下错误:
事实上,从解决问题来看,问题的原因似乎与特定的状态设置器setFirstAuthorId有关。无论我将它设置为什么,以及从我在组件中的哪个位置设置它,它都会引发屏幕截图中显示的错误。
setFirstAuthorId("anything") // will throw an error
因此,作为一种解决方法(这并不理想),我创建了一个条件(三元表达式),它会在图书提交时(bookEntry 对象状态提交给 API)检查是否没有 authorId ,然后将 bookEntry 状态的 authorId 属性设置为第一作者 (Patrick Rothfuss) 的硬编码 authorId。理想情况下,firstAuthorId 状态应该设置为此而不是硬编码的 ID。
function submitBook(event) {
const filledAuthorId = bookEntry.authorId? bookEntry.authorId : "5ed44e015ecb7c42a0bf824d";
addBook({ variables: {
name: bookEntry.name,
genre: bookEntry.genre,
authorId: filledAuthorId
},
refetchQueries: [{ query: GET_ALL_BOOKS_QUERY }]
})
完整代码:
import React, { useState } from 'react';
import { useQuery, useMutation } from '@apollo/react-hooks';
import { GET_ALL_AUTHORS_QUERY, ADD_BOOK_MUTATION, GET_ALL_BOOKS_QUERY } from '../queries/queries';
function AddBook() {
const { loading, error, data } = useQuery(GET_ALL_AUTHORS_QUERY);
// to call more than one query using the useQuery, call useQuery hook but give alias names to loading, error, data to avoid queries overwriting each other's returned fields
// const { loading: loadingAddBook, error: errorAddBook, data: dataAddBook} = useQuery(A_SECOND_QUERY)
const [ addBook ] = useMutation(ADD_BOOK_MUTATION);
const [bookEntry, setBookEntry] = useState({
name: "",
genre: "",
authorId: ""
});
const [firstAuthorId, setFirstAuthorId] = useState("");
function handleChange(event) {
const { name, value } = event.target;
console.log(`handleChange event: ${event.target.value}`);
setBookEntry(preValue => {
return {
...preValue,
[name]: value
}
});
}
function submitBook(event) {
const filledAuthorId = bookEntry.authorId? bookEntry.authorId : "5ed44e015ecb7c42a0bf824d";
addBook({ variables: {
name: bookEntry.name,
genre: bookEntry.genre,
authorId: filledAuthorId
},
refetchQueries: [{ query: GET_ALL_BOOKS_QUERY }]
})
.then(data => {
console.log(`Mutation executed: ${JSON.stringify(data)}`);
setBookEntry(prevValue => ({
...prevValue,
name: "",
genre: ""
}));
})
.catch(err => console.log(err));
event.preventDefault();
}
function displayAuthors() {
if (loading) return (<option disabled>'Loading authors...'</option>);
if (error) return (`Error: ${error.message}`);
return data.authors.map((author, index) => {
if (index === 0) {
setFirstAuthorId(author.id);
}
return (
<option key={author.id} value={author.id}>{author.name}</option>
);
});
}
return (
<form id="add-book" onSubmit={submitBook}>
<h1>Add a new book to Wiki Books</h1>
<div className="field">
<label>Book name: </label>
<input type="text" onChange={handleChange} name="name" required value={bookEntry.name}/>
</div>
<div className="field">
<label>Genre: </label>
<input type="text" onChange={handleChange} name="genre" required value={bookEntry.genre}/>
</div>
<div className="field">
<label>Author (select author): </label>
<select onChange={handleChange} name="authorId">
{displayAuthors()}
</select>
</div>
<button type="submit">+</button>
</form>
);
}
export default AddBook;
【问题讨论】:
标签: javascript reactjs react-hooks