我有两种类型的问题的测验组件(一个正确的答案和问题与免费答案)。我需要向后端发送以下格式的答案:
[
{
questionId: 'test-id',
answers: ['answerId']// send answer id if this is question with one correct answer
},
{
questionId: 'test-id',
answers: ['answerId']// send answer id if this is question with one correct answer
},
{
questionId: 'test-id-2',
freeAnswer: 'some text' // send freeAnswer if it is open ended question
}
...
]我创建了两个处理程序:一个用于文本区域,另一个用于单选按钮,
const handleOptionChange = ( question, answer) => {
onChangeQuestionAnswer(question, answer, 'oneCorrectAnswer')
}
const handleFreeAnswerChange = (value, question) => {
onChangeQuestionAnswer(question, value, 'freeAnswer')
}在父组件中有一个通用的处理程序来处理所有的回答:
const [chosenAnswers, setChosenAnswers] = useState([])
const handleChangeQuestionAnswer = (
questionId,
answerId,
type
) => {
const foundedId = chosenAnswers.find(item => item.id === questionId)
if (!foundedId) {
if (type === 'oneCorrectAnswer') {
setChosenAnswers([...chosenAnswers, { id: questionId, answers: [answerId] }])
} else {
setChosenAnswers([...chosenAnswers, { id: questionId, freeAnswer: answerId }])
}
} else {
const newResultArray = chosenAnswers.map(item => {
if (item.id !== questionId) {
return item
}
if (type === 'oneCorrectAnswer') {
return {
...item,
answers: [answerId]
}
} else {
return {
...item,
freeAnswer: answerId
}
}
})
setChosenAnswers(newResultArray)
}
}然后我就把chosenAnswers发送给API。这种方法很有效,但对我来说它看起来很奇怪,而且开销很大,我可以以某种方式简化这种逻辑吗?
发布于 2020-12-02 11:52:39
数据和处理程序看起来很干净。在父组件中,有一些逻辑重复了四次(返回answer或freeAnswer键以及相关的字符串或数组)。我会把它放到一个变量中:
const answerForm =
type === 'oneCorrectAnswer'
? { answers: [answerId] }
: { freeAnswer: answerId };然后在调用setChosenAnswers时传播该变量,或者在chosenAnswers上映射时返回对象。例如。
[...chosenAnswers, { id: questionId, ...answerForm }]这也允许您删除两个if/else,因为除了那个重复的逻辑之外,条件是相同的。
您还可以通过两种方式修改foundedId if/else:
对顺序进行
answerToSubmit)的side 调用setChosenAnswsers一次而不是两次
const answerToSubmit = foundedId
? chosenAnswers.map((item) => {
if (item.id !== questionId) return item;
return {
...item,
...answerForm,
};
})
: [...chosenAnswers, { id: questionId, ...answerForm }];完整代码:
const [chosenAnswers, setChosenAnswers] = useState([]);
const handleChangeQuestionAnswer = (questionId, answerId, type) => {
const foundedId = chosenAnswers.find((item) => item.id === questionId);
const answerForm =
type === 'oneCorrectAnswer'
? { answers: [answerId] }
: { freeAnswer: answerId };
const answerToSubmit = foundedId
? chosenAnswers.map((item) => {
if (item.id !== questionId) return item;
return {
...item,
...answerForm,
};
})
: [...chosenAnswers, { id: questionId, ...answerForm }];
setChosenAnswers(answerToSubmit);
};如果您有更多的答案类型,我可能会建议使用switch语句,但总的来说,这会减少重复的逻辑,并使代码更简洁。
发布于 2020-12-02 11:58:45
你可以通过使用ES6功能来重构你的代码,让它更容易阅读,也许文本区域和单选按钮的拆分更改答案处理程序会更好?一个函数应该只做一件事,不要使用太多的if/else语句,如果是我,我会这样修改代码:
const handleOptionChange = (question, answer) => {
onChangeSelectionQuestionAnswer(question, answer)
}
const handleFreeAnswerChange = (value, question) => {
onChangeFreeTextQuestionAnswer(question, value)
}
const handleChangeFreeTextQuestionAnswer = (questionId, answer) => {
const targetAnswer = chosenAnswers.find(item => item.id === questionId)
let newAnswers = [...chosenAnswers]
if (!targetAnswer) {
newAnswers.push({ id: questionId, freeAnswer: answer })
}
if (targetAnswer) {
const idx = newAnswers.indexOf(targetAnswer)
newAnswers[idx].freeAnswer = answer
}
setChosenAnswers(newAnswers)
}
const handleChangeSelectionQuestionAnswer = (questionId, answerId) => {
const targetAnswer = chosenAnswers.find(item => item.id === questionId)
let newAnswers = [...chosenAnswers]
if (!targetAnswer) {
newAnswers.push({ id: questionId, answers: [answerId] })
}
if (targetAnswer) {
const idx = newAnswers.indexOf(targetAnswer)
newAnswers[idx].answers = [answerId]
}
setChosenAnswers(newAnswers)
}你会发现handleChangeFreeTextQuestionAnswer和handleChangeSelectionQuestionAnswer有重复的代码,所以你可以进一步简化
const handleChangeFreeTextQuestionAnswer = (questionId, answer) => {
handleChangeQuestionAnswer(questionId, answer, 'freeAnswer')
}
const handleChangeSelectionQuestionAnswer = (questionId, answerId) => {
handleChangeQuestionAnswer(questionId, [answer], 'answers')
}
const handleChangeQuestionAnswer = (questionId, newValue, valueField) => {
const targetAnswer = chosenAnswers.find(item => item.id === questionId)
let newAnswers = [...chosenAnswers]
if (!targetAnswer) {
newAnswers.push({ id: questionId, [valueField]: newValue })
}
if (targetAnswer) {
const idx = newAnswers.indexOf(targetAnswer)
newAnswers[idx][valueField]= newValue
}
setChosenAnswers(newAnswers)
}如果以后要添加新的题型,只需要添加一个新的handleChangeXXXQuestionAnswer函数,然后调整答案格式和更新字段,调用handleChangeQuestionAnswer,就不需要再添加更多的if/else或者switch语句了。
发布于 2020-12-02 12:05:59
因为您已经有了单独的处理程序,所以我建议只将格式化的answer对象传递给handleChangeQuestionAnswer。例如,您可以将专用问题类型处理程序更改为以下内容
const handleOptionChange = ( id, answer) => {
onChangeQuestionAnswer({id, answers: [answer]})
}
const handleFreeAnswerChange = (freeAnswer, id) => {
onChangeQuestionAnswer({id, freeAnswer})
}至于通用处理程序,您可以使用对象而不是数组来跟踪答案。对于对象,您可以使用与数组相同的扩展语法。多亏了上面的其他更新,你可以真正地将你的通用处理程序简化到一行。请在下面更新函数
const [chosenAnswers, setChosenAnswers] = useState({})
const handleChangeQuestionAnswer = (question) => {
setChosenAnswers(answers => {...answers, ...{[question.id]: question}})
}注意:我使用一个处理程序来更新状态,以避免任何争用条件。AFAIK这始终是使用钩子设置器函数更新状态的首选方法。
在向服务器提交答案时,使用Object.values()获取数组形式的值。例如:
Object.values(chosenAnswers)https://stackoverflow.com/questions/65101641
复制相似问题