React开发待办App时出现Uncaught TypeError报错求助
React 待办应用报错:Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
执行npm start后触发该报错,相关代码如下:
App.js 代码
import React from 'react'; import './App.css'; import AddItem from './addItem'; import Items from './list'; import './events'; function App() { return ( <body> <h1> To Do List </h1> <div > <AddItem/> <Items/> </div> </body> ); } export default App;
events.js 代码
let addBtn=document.getElementById('add'); let removeBtn=document.getElementById('remove'); let listOfItems=document.getElementById('items'); let addText=document.getElementById('addText'); let removeText=document.getElementById('removeText'); addBtn.addEventListener('click',add); function add(){ if(addText.value===''){ alert("Type the item you want to add first"); }else{ let added=document.createElement('LI'); let addedTextNode=document.createTextNode(addText.value); added.appendChild(addedTextNode); listOfItems.appendChild(added); localStorage.setItem('items',added.innerHTML); } }
问题根源
- DOM元素未加载完成:
events.js在模块加载时立即执行document.getElementById,此时React组件还没完成渲染,DOM节点(比如addBtn)还不存在,导致获取结果为null,调用addEventListener触发报错。 - 开发范式冲突:混用原生DOM操作和React组件化逻辑,违背了React"状态驱动UI"的核心原则,容易引发时序问题和维护困难。
修复方案
1. 移除events.js文件
React不需要单独的原生事件监听文件,所有交互逻辑应整合到组件内部。
2. 重构AddItem组件,内置添加逻辑
修改addItem.js,通过React的状态管理和事件绑定实现添加功能:
import React, { useState } from 'react'; function AddItem({ onAddItem }) { const [inputText, setInputText] = useState(''); const handleAddClick = () => { if (!inputText.trim()) { alert("Type the item you want to add first"); return; } onAddItem(inputText); setInputText(''); // 清空输入框 }; return ( <div> <input id="addText" type="text" value={inputText} onChange={(e) => setInputText(e.target.value)} placeholder="输入待办事项" /> <button id="add" onClick={handleAddClick}>添加</button> </div> ); } export default AddItem;
3. 在App组件中维护待办状态
修改App.js,负责管理待办列表的状态,并同步到localStorage:
import React, { useState, useEffect } from 'react'; import './App.css'; import AddItem from './addItem'; import Items from './list'; function App() { // 从localStorage加载初始待办数据 const [items, setItems] = useState(() => { const saved = localStorage.getItem('items'); return saved ? JSON.parse(saved) : []; }); // 状态变化时自动保存到localStorage useEffect(() => { localStorage.setItem('items', JSON.stringify(items)); }, [items]); // 处理新增待办的逻辑 const handleAddItem = (newItem) => { setItems(prev => [...prev, newItem]); }; return ( <div className="App"> <h1>To Do List</h1> <AddItem onAddItem={handleAddItem} /> <Items items={items} /> </div> ); } export default App;
4. 重构Items组件渲染列表
修改list.js,接收父组件传递的items并渲染:
import React from 'react'; function Items({ items }) { return ( <ul id="items"> {items.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> ); } export default Items;
额外注意事项
- React组件的返回值必须是单个根元素,原代码中返回
<body>不符合规范,改用<div>或React片段<>包裹。 - 避免直接操作DOM,所有UI更新通过修改状态实现,这是React的核心开发模式。
- 存储到
localStorage时,使用JSON.stringify和JSON.parse处理数组,确保完整保存整个待办列表,而非单个元素的HTML内容。
内容的提问来源于stack exchange,提问作者Mohamed Hamissa
相关产品推荐
相关产品推荐

