EP149. “点击判分与 React useState 管理组件状态”
🔒 登录后可标记已读给前台答案列表接上真正的点击判分逻辑:点击某个答案,对比它的下标是否等于 correctAnswer,正确/错误分别显示带图标的提示语。核心知识点是 React 的 useState——不能像原生 JS/jQuery 那样直接找到 DOM 元素手动加减 class,而是要让组件自己维护一份「状态」(这里是 isCorrect:undefined/true/false),状态一变化 React 就会自动重新渲染,JSX 里用状态值驱动要不要显示某个提示。
涉及文件
wp-content/plugins/are-you-paying-attention/src/frontend.js(修改)
代码实现
src/frontend.js(完整文件):
import React, {useState} from 'react'
import ReactDOM from 'react-dom'
import "./frontend.scss"
const divsToUpdate = document.querySelectorAll(".paying-attention-update-me")
divsToUpdate.forEach(function(div) {
const data = JSON.parse(div.querySelector("pre").innerHTML)
ReactDOM.render(<Quiz {...data} />, div)
div.classList.remove("paying-attention-update-me")
})
function Quiz(props) {
const [isCorrect, setIsCorrect] = useState(undefined)
function handleAnswer(index) {
if (index == props.correctAnswer) {
setIsCorrect(true)
} else {
setIsCorrect(false)
}
}
return (
<div className="paying-attention-frontend">
<p>{props.question}</p>
<ul>
{props.answers.map(function(answer, index) {
return <li onClick={() => handleAnswer(index)}>{answer}</li>
})}
</ul>
<div className={"correct-message" + (isCorrect == true ? " correct-message--visible" : "")}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" className="bi bi-emoji-smile" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="M4.285 9.567a.5.5 0 0 1 .683.183A3.498 3.498 0 0 0 8 11.5a3.498 3.498 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.498 4.498 0 0 1 8 12.5a4.498 4.498 0 0 1-3.898-2.25.5.5 0 0 1 .183-.683zM7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5zm4 0c0 .828-.448 1.5-1 1.5s-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5z"/>
</svg>
<p>That is correct!</p>
</div>
<div className={"incorrect-message" + (isCorrect === false ? " correct-message--visible" : "")}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" className="bi bi-emoji-frown" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="M4.285 12.433a.5.5 0 0 0 .683-.183A3.498 3.498 0 0 1 8 10.5c1.295 0 2.426.703 3.032 1.75a.5.5 0 0 0 .866-.5A4.498 4.498 0 0 0 8 9.5a4.5 4.5 0 0 0-3.898 2.25.5.5 0 0 0 .183.683zM7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5zm4 0c0 .828-.448 1.5-1 1.5s-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5z"/>
</svg>
<p>Sorry, try again.</p>
</div>
</div>
)
}
关键改动点:
import React, {useState} from 'react':useState是 React 提供的 Hook,专门用来给函数组件添加「状态」这个能力const [isCorrect, setIsCorrect] = useState(undefined):这行用了数组解构语法。useState(初始值)返回一个包含两个元素的数组:第一个是当前状态值(isCorrect),第二个是专门用来更新这个状态的函数(setIsCorrect)——初始值给undefined,表示「用户还没点过任何答案」- React 状态的核心规则:永远不直接读写 DOM,只通过状态驱动界面。点击答案后调用
setIsCorrect(true/false)更新状态,React 侦测到状态变化后会自动重新执行整个组件函数、根据最新的状态值重新计算该显示什么——不需要自己写「找到某个 div,手动加/删 class」这类命令式代码 handleAnswer(index):对比点击的答案下标跟props.correctAnswer是否相等,分别调用setIsCorrect(true)或setIsCorrect(false)<li onClick={() => handleAnswer(index)}>:同样要用箭头函数包一层再调用(跟编辑器端的删除按钮同理),否则会在渲染时立即执行而不是等点击时才执行- 两个提示
<div>的className用字符串拼接 + 三元运算符动态决定要不要加上--visible修饰符 class:isCorrect == true时给「正确」提示加上可见 class;isCorrect === false时给「错误」提示加上可见 class——这里特意用===(严格相等)而不是==,是为了避免undefined(初始状态)被宽松相等判断误判成false,导致页面一加载就显示「错误」提示 - 图标直接从开源图标库(Bootstrap Icons,
icons.getbootstrap.com)复制 SVG 代码粘贴进 JSX——原始 SVG 里的class属性要手动改成 JSX 认的className,fill="currentColor"这类属性按需删除保留原始配色 - CSS 负责淡入淡出的过渡动画和自动消失的计时(转录提到大约 2.5 秒后消失),这部分是纯 CSS 动画,跟 React/JS 逻辑无关,只由 class 的增删触发
- 目前有个已知的小限制:一旦
isCorrect被设置成false(答错一次),如果再点别的错误答案,状态值仍然是false,没有变化,React 不会重新触发渲染——所以连续点错时不会重复看到「错误」提示的淡入动画,这个细节问题在后续讲座里处理
[截图:前台页面点击某个答案后,出现的"That is correct!"或"Sorry, try again."提示消息(含笑脸/哭脸 SVG 图标)]
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
useState(initialValue) | React Hook | 给函数组件添加状态,返回 [当前值, 更新函数] 数组 |
数组解构 const [a, b] = arr | JS 语法 | 按位置把数组元素分别赋值给多个变量 |
常见坑
- 试图直接用
document.querySelector()找到 DOM 元素手动加减 class 来实现「显示/隐藏提示」——这是 React 组件里应该避免的命令式操作,正确做法是用状态驱动渲染结果,让 React 自己决定最终输出什么 HTML - 用
==判断「是否等于false」——undefined == false在 JS 里的宽松相等判断下不成立,但如果检查逻辑写得不够严谨容易让初始状态被误判,用===严格相等更安全 onClick直接写函数调用onClick={handleAnswer(index)}——会在组件渲染阶段立即执行,而不是等待用户真正点击
延伸 / 后续讲座会用到
下一讲会打磨一些交互细节(比如允许连续修改答案、样式收尾等),把前台体验做得更完整。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 25, EP149