React FAQ页面按钮交互异常:点击单个按钮全部展开问题排查
解决FAQ按钮点击联动展开问题
我在搭建FAQs页面时设置了多个按钮,预期点击每个按钮仅展开其下方对应的隐藏段落,但现在点击任意一个按钮,所有按钮对应的段落都会一起展开。以下是我当前使用的React代码:
class FAQ extends React.Component { constructor () { super() this.state = { isHidden: true, } } toggleHidden () { this.setState({ isHidden: !this.state.isHidden}) } render() { return ( <div> <div className="faq--button"> <button onClick={this.toggleHidden.bind(this)}>button to click</button> {!this.state.isHidden && <p>lorem ipsum</p>} </div> {/* 多个重复的faq--button块 */} </div> ) } }
问题根源
当前用了一个全局的isHidden状态,所有FAQ按钮和内容都依赖这个状态值,所以只要点击任意按钮修改状态,所有内容都会同步展开/收起。
解决方案
方法一:拆分独立的FAQ项组件
把单个FAQ的按钮和对应内容封装成独立组件,每个组件维护自己的展开状态,互相不会影响。
// 单个FAQ项组件 class FAQItem extends React.Component { constructor(props) { super(props); this.state = { isHidden: true }; // 提前绑定this,避免每次render重新绑定 this.toggleHidden = this.toggleHidden.bind(this); } toggleHidden() { this.setState(prevState => ({ isHidden: !prevState.isHidden })); } render() { const { question, answer } = this.props; return ( <div className="faq--button"> <button onClick={this.toggleHidden}>{question}</button> {!this.state.isHidden && <p>{answer}</p>} </div> ); } } // 父组件批量渲染FAQ项 class FAQ extends React.Component { render() { // 可以把FAQ数据抽离出来,方便维护 const faqList = [ { question: "第一个问题", answer: "这是第一个问题的解答内容" }, { question: "第二个问题", answer: "这是第二个问题的解答内容" }, { question: "第三个问题", answer: "这是第三个问题的解答内容" } ]; return ( <div className="faq-container"> {faqList.map((item, idx) => ( <FAQItem key={idx} question={item.question} answer={item.answer} /> ))} </div> ); } }
方法二:父组件维护多状态(不拆分组件)
如果不想拆分组件,可以在父组件的state里用数组或对象记录每个FAQ项的展开状态,通过索引或唯一标识区分不同项。
class FAQ extends React.Component { constructor() { super(); // 用数组存储每个FAQ项的隐藏状态,初始都为true(隐藏) this.state = { hiddenStates: [true, true, true] }; this.toggleHidden = this.toggleHidden.bind(this); } toggleHidden(index) { this.setState(prevState => { // 复制原数组,避免直接修改state const newStates = [...prevState.hiddenStates]; // 切换对应索引项的状态 newStates[index] = !newStates[index]; return { hiddenStates: newStates }; }); } render() { const faqList = [ { question: "第一个问题", answer: "这是第一个问题的解答内容" }, { question: "第二个问题", answer: "这是第二个问题的解答内容" }, { question: "第三个问题", answer: "这是第三个问题的解答内容" } ]; return ( <div className="faq-container"> {faqList.map((item, idx) => ( <div key={idx} className="faq--button"> {/* 点击时传递当前项的索引 */} <button onClick={() => this.toggleHidden(idx)}>{item.question}</button> {/* 根据对应索引的状态判断是否显示内容 */} {!this.state.hiddenStates[idx] && <p>{item.answer}</p>} </div> ))} </div> ); } }
两种方法对比
- 拆分组件更符合React组件化思想,每个组件职责单一,后续修改或扩展单个FAQ项更方便;
- 父组件维护状态适合FAQ数量较少、需要统一控制所有项展开/收起的场景。
内容的提问来源于stack exchange,提问作者Zubi
相关产品推荐
相关产品推荐

