如何让goUp与goDown函数在.jsx文件中正常工作?
解决React中textarea滚动控制失效的问题
问题根源
你代码里的核心问题是初始化时过早获取DOM元素:在组件顶层直接调用document.getElementById("textt"),此时React还未完成DOM渲染,okayy的值为null。点击按钮时传递的是null,滚动方法自然无法执行。即使后续DOM渲染完成,组件状态更新导致重新渲染时,okayy会被重新赋值,仍可能出现获取不到元素的情况。
修复方案
使用React官方推荐的useRef钩子来获取DOM元素,它能在组件生命周期内稳定保存DOM引用,避免上述问题。
修改后的完整代码
import { useState, useRef } from 'react' // 新增useRef导入 import Dialog from '@mui/material/Dialog' import Button from '@mui/material/Button' import CircularProgress from '@mui/material/CircularProgress' import CloseIcon from '@mui/icons-material/Close' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp' import { styled } from '@mui/material/styles' import { UpdateBookModalForm } from './updateBookModalSkin' import { updateBook } from 'api/book' import Toast from 'shared/Toast' import { queryClient } from 'App' import AppContainer from '../../appSkin.js' // 补全BootstrapDialog的定义(原代码未导入,需确保样式正确) const BootstrapDialog = styled(Dialog)(({ theme }) => ({ // 若无自定义样式可直接使用Dialog组件 })) const UpdateBookModal = ({ book_id, book_title, book_author, book_issn, book_note, handleClose, modalOpen, }) => { // 用useRef创建textarea的DOM引用,替代document.getElementById const textareaRef = useRef(null); // 补充状态初始值,优化组件初始化体验 const [bookTitle, setBookTitle] = useState(book_title) const [author, setAuthor] = useState(book_author) const [issn, setIssn] = useState(book_issn) const [note, setNote] = useState(book_note) const [bookUpdating, setBookUpdating] = useState(false) const [toastMessage, setToastMessage] = useState('') const [toastType, setToastType] = useState('') const [toastOpen, setToastOpen] = useState(false) const goUp = (element) => { if (!element) return; // 增加空值判断,避免报错 if (element.scrollTop !== 0) { element.scrollTo({ top: element.scrollTop - 10, left: 0, behavior: "smooth" }); } }; const goDown = (element) => { if (!element) return; // 增加空值判断 element.scrollTo({ top: element.scrollTop + 10, left: 0, behavior: "smooth" }); }; const handleSubmit = async ev => { ev.preventDefault() setBookUpdating(true) const result = await updateBook(book_id, { book_title: bookTitle, book_author: author, book_issnisbn: issn, book_notes: note, }) setBookUpdating(false) handleClose() setToastOpen(true) if (result.error) { setToastType('error') if (result.error.response) { setToastMessage(result.error.response.data.message) } else if (result.error.request) { setToastMessage(result.error.request) } else { setToastMessage(result.error.message) } } else { setToastType('success') setToastMessage('Book updated successfully!') queryClient.invalidateQueries('books') } } const toastClose = (event, reason) => { if (reason === 'clickaway') { return } setToastOpen(false) } return ( <BootstrapDialog onClose={handleClose} aria-labelledby="customized-dialog-title" open={modalOpen} > <UpdateBookModalForm onSubmit={handleSubmit}> <AppContainer> <div className="modal_top"> <input style={{ lineHeight: 2.5 }} className="h8 input-box font" type="text" placeholder="TITLE" value={bookTitle} onChange={ev => setBookTitle(ev.target.value)} /> <span className="close-icon" onClick={handleClose}> <CloseIcon /> </span> </div> {/* 修改点击事件,传递ref.current */} <div className="top" onClick={() => goUp(textareaRef.current)}> <KeyboardArrowUpIcon style={{ cursor: 'pointer' }} /> </div> {/* 将ref绑定到textarea元素 */} <textarea ref={textareaRef} rows="4" cols="50" maxLength="400" className="h8 text-box font" placeholder="YOUR NOTES" value={note} onChange={ev => setNote(ev.target.value)} /> <div className="bottom" onClick={() => goDown(textareaRef.current)}> <KeyboardArrowDownIcon style={{ cursor: 'pointer' }} /> </div> <div className="modal_bottom"> <div className='note_buttons_container'> <div className="h7 note">Please note that in most cases we can retrieve <br /> Metadata directly from the uploaded file</div> <div className="buttons"> <Button onClick={handleClose} variant="contained" className="h6 login-btn font" > Cancel </Button> <Button type="submit" variant="contained" className="h6 login-btn" disabled={bookUpdating} > {bookUpdating ? ( <CircularProgress style={{ height: '28px', width: '28px' }} color="inherit" /> ) : ( 'Save' )} </Button> </div> </div> </div> </AppContainer> </UpdateBookModalForm> <Toast toastClose={toastClose} open={toastOpen} severity={toastType} message={toastMessage} /> </BootstrapDialog> ) } export default UpdateBookModal
关键改动说明
- 使用
useRef获取DOM元素:替代document.getElementById,确保在DOM挂载后能稳定获取到textarea的引用,不受组件重新渲染影响。 - 绑定ref到textarea:通过
ref={textareaRef}将DOM元素与ref关联,建立可靠的引用关系。 - 传递有效元素引用:点击按钮时传递
textareaRef.current,保证每次调用滚动方法时都能拿到最新的DOM元素。 - 增加空值防御:在滚动函数中先判断元素是否存在,避免空指针报错。
- 补充状态初始值:将props传入的默认值赋值给state,优化组件初始化时的显示效果。
内容的提问来源于stack exchange,提问作者NetOceanSurfer
相关产品推荐
相关产品推荐

