React开发:如何从Dropdown组件传递接收选中关联的图书ID状态
实现思路
React采用单向自上而下的数据流规则,跨组件取关联值不要通过DOM操作(比如你之前写的event.target.parentElement.id)实现,这种方式和React的声明式设计相悖,一旦DOM结构调整就会直接失效。
具体实现流程分为三步:
1. 重构DropDown子组件
将DropDown改为受控组件,不再在内部维护和业务相关的状态,通过props接收所需参数与状态变更的回调:
bookId:对应每本图书的唯一idcurrentShelf:图书当前所属的书架值,作为下拉框的默认选中项onChange:用户选择选项时触发的回调,将选中的新书架值、关联的图书id共同传递给父组件
重构后的DropDown组件代码
import React, { Component } from 'react'; class DropDown extends Component { handleChange = (event) => { const selectedShelf = event.target.value; // 触发父组件传入的回调,传递关联的图书id和选中的书架值 this.props.onChange(this.props.bookId, selectedShelf); }; render() { return ( <select value={this.props.currentShelf} onChange={this.handleChange} > <option value="move" disabled>Move to:</option> <option value="currentlyReading">Currently Reading</option> <option value="wantToRead">Want to Read</option> <option value="read">Read</option> <option value="none">None</option> </select> ); } } export default DropDown;
2. 在父组件中定义统一的状态处理方法
在BooksApp组件内编写统一的下拉选择处理函数,接收从子组件上传的bookId和newShelf值,后续可以在这个函数内完成接口更新、本地状态刷新等业务逻辑。
3. 为DropDown组件绑定对应props
在遍历每本图书渲染节点时,将当前图书的id、书架值、绑定的处理回调作为props传给DropDown组件,替代原有的无参渲染方式。
调整后的BooksApp核心代码
import React, { Component } from 'react'; import DropDown from './comps/Dropdown'; import * as BooksAPI from './data/BooksAPI'; class BooksApp extends Component { state = { currentlyReading: [], wantToRead: [], read: [], search: [], reset: [], error: false }; componentDidMount = () => { BooksAPI.getAll() .then(books => { // 按图书自带的shelf字段分类,替代原来全量塞到wantToRead的逻辑 const shelfGroups = { currentlyReading: [], wantToRead: [], read: [] }; books.forEach(book => { if (shelfGroups[book.shelf]) shelfGroups[book.shelf].push(book); }); this.setState({ ...shelfGroups }); }) .catch(err => { this.setState({ error: true }); }); }; // 处理下拉框选择变更的统一入口 handleShelfChange = (bookId, newShelf) => { // 这里可以拿到完整的关联图书id和选中的书架值,实现后续业务逻辑 console.log('关联图书id:', bookId, '选中的新书架:', newShelf); // 示例:调用图书更新接口 // BooksAPI.update({id: bookId}, newShelf).then(() => { /* 刷新本地图书列表状态 */ }) }; render() { return ( <div> <div className="currentlyReading shelf"> <h2>Currently</h2> {this.state.currentlyReading.map(reading => ( <div className="item" key={reading.id} id={reading.id}> <img src={reading.imageLinks && reading.imageLinks.smallThumbnail} alt="img"/> {reading.title}<br></br> {reading.publisher} <img src='\icons\arrow-drop-down.svg' className='toggle'/> <DropDown bookId={reading.id} currentShelf={reading.shelf} onChange={this.handleShelfChange} /> </div> ))} </div> <div className="wantToRead shelf"> <h2>Want To</h2> {this.state.wantToRead.map(wishList => ( <div className="item" key={wishList.id} id={wishList.id}> <img src={wishList.imageLinks && wishList.imageLinks.smallThumbnail} alt="img"/> {wishList.title} <br></br> {wishList.publisher} <img src='\icons\arrow-drop-down.svg' className='toggle'/> <DropDown bookId={wishList.id} currentShelf={wishList.shelf} onChange={this.handleShelfChange} /> </div> ))} </div> <div className="read shelf"> <h2>Read</h2> {this.state.read.map(finished => ( <div className="item" key={finished.id} id={finished.id}> <img src={finished.imageLinks && finished.imageLinks.smallThumbnail} alt="img"/> {finished.title}<br></br> {finished.publisher} <img src='\icons\arrow-drop-down.svg' className='toggle'/> <DropDown bookId={finished.id} currentShelf={finished.shelf} onChange={this.handleShelfChange} /> </div> ))} </div> </div> ); } } export default BooksApp;
附带修复的原有代码问题
- 列表渲染时给每个外层节点绑定了图书
id作为key,避免列表渲染的性能问题,替代原来用title当唯一标识的不严谨写法 - 移除了通过DOM父级元素取业务值的逻辑,完全以React状态驱动视图,降低组件间的耦合度
- 修复了原DropDown组件内
state = { value: id }的未定义变量语法错误 - 修正了
componentDidMount中将所有接口返回的图书全量塞入wantToRead列表的逻辑,按图书自带的书架字段做分类渲染
内容的提问来源于stack exchange,提问作者Lieutenant Dan
相关产品推荐
相关产品推荐

