React应用初始正常、搜索时报错:Cannot read property 'filter' of undefined
函数组件中useState返回的状态更新方法会完全替换整个状态值,不会像类组件的setState那样对顶层属性做自动浅合并。
你初始化的状态是包含users、searchField两个字段的对象,但在onSearchChange触发时,调用setState只传入了{ searchField: e.target.value },直接把整个状态替换为仅含searchField的新对象,原有的users字段被彻底移除。下一轮渲染时,state.users变为undefined,调用.filter方法就会抛出你遇到的TypeError。
为什么初始渲染正常?因为第一次渲染时状态还是初始化的完整对象,
state.users存在且可用;第一次触发搜索输入、状态更新后,users从状态中消失,后续渲染就会触发报错。你之前观察到filteredUsers输出正常,是因为初始渲染阶段的状态是完整的,触发搜索后的新渲染周期刚访问到state.users就直接抛错中断,根本没机会打印异常状态下的filteredUsers值。
方案1:修正状态更新逻辑,避免丢失原有属性
如果要保留当前把所有值存在同一个状态对象里的写法,更新状态时需要手动保留原有属性,不能直接替换整个状态对象:
const onSearchChange = (e) => { // 基于上一轮的状态做浅合并,保留原有的users字段 setState(prevState => ({ ...prevState, searchField: e.target.value })); };
这里使用函数式更新拿到上一轮的状态值,通过对象展开保留所有原有属性,仅覆盖需要更新的searchField值,就不会出现字段丢失的问题。
方案2(更推荐):拆分独立状态,移除无需存入状态的静态数据
你导入的users是固定的静态数据,不会随用户交互变化,完全没必要存在组件状态里。把它从状态中移除,仅将会动态变化的searchField作为独立状态值,从根源上避免状态更新时的字段丢失问题,也让逻辑更清晰:
修改后的核心代码逻辑:
const App = () => { // 仅将动态变化的搜索词存入状态,静态的users直接使用导入的常量即可 const [searchField, setSearchField] = useState(''); const filteredUsers = users.filter((user) => { return user.name.toLowerCase().includes(searchField.toLowerCase()); }); const onSearchChange = (e) => { // 直接更新搜索词状态,不存在替换整个对象丢字段的风险 setSearchField(e.target.value); }; return ( <div className='App tc'> <h1>RoboFriends</h1> <SearchBox searchChange={onSearchChange} /> <CardList robots={filteredUsers} /> </div> );
当前SearchBox组件虽然定义了searchField的入参,但实际并未使用。完成上面的状态拆分后,可以将searchField值传给输入框的value属性,把输入框改造为完全受控组件,避免输入状态和组件状态不一致的问题。
内容的提问来源于stack exchange,提问作者user6248190

