React app throws TypeError: Cannot read properties of undefined (reading 'name') when compiling

React app throws TypeError: Cannot read properties of undefined (reading 'name') when compiling

当我从列表中编辑名称时,我的 CRUD 应用程序一直 运行 出现问题,它抛出错误“TypeError:无法读取未定义的属性(读取 'name') ".

它似乎源于我输入字段中的“值={selectedUser.name}”,但是当我删除它时,应用程序编译但我根本无法更改该字段 - 输入时没有任何反应。感谢您的帮助!

import React, { useState, useContext, useEffect } from 'react'
import { GlobalContext } from '../context/GlobalState'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { Form, FormGroup, Label, Input, Button } from 'reactstrap'

const EditUser = (props) => {
const [selectedUser, setSelectedUser] = useState({
  id: '',
  name: ''
});
const { users, editUser } = useContext(GlobalContext);
const navigate = useNavigate();
const currentUserId = useParams(props);

useEffect(() => {
  const userId = currentUserId;
  const selectedUser = users.find(user => user.id === userId)
  setSelectedUser(selectedUser)
}, [currentUserId, users])

const onSubmit = (e) => {
  e.preventDefault()
  editUser(selectedUser)
  navigate('/');
}

const onChange = (e) => {
  setSelectedUser({...selectedUser, [e.target.name]: e.target.value})
}

  return (
    <div>
        <Form onSubmit={onSubmit}>
            <FormGroup>
                <Label>Edit Name</Label>
                <Input type='text' name='name' value={selectedUser.name} onChange={onChange} placeholder='Enter Name'></Input>
            </FormGroup>
        <Button type='submit'>Edit Name</Button>
        <Link to='/' className='btn btn-danger m-2'>Back</Link>
        </Form>
    </div>
  )
}

export default EditUser

如果 currentUserId 出于任何原因是 undefined,或者您的 users.find() returns undefined,那么您将存储 undefined 在你的 selectedUser 状态变量中。

根据您的评论,您似乎无法使用 optional chainingfoo?.bar 语法),那么您可能只想从源头上防止这种情况发生:

  useEffect(() => {
    const userId = currentUserId;
    const foundUser = users.find((user) => user.id === userId);
    if (foundUser) {
      setSelectedUser(foundUser);
    } 
  }, [currentUserId, users]);

另请注意,我在您的 useEffect 中重命名了您的变量,以避免与更高级别的 selectedUser 混淆。

问题

Array.prototype.find returns 第一个匹配元素,或者 undefined 如果没有找到这样的匹配。

const selectedUser = users.find(user => user.id === userId); // potentially undefined

此外,useParams 钩子的返回值是 key-value 对任何已定义路由路径参数的对象。该代码似乎错误地假定返回值 来自路径的 currentUserId 参数,并使用它来搜索 users数组。

const currentUserId = useParams(props);

useEffect(() => {
  const userId = currentUserId;
  const selectedUser = users.find(user => user.id === userId);
  setSelectedUser(selectedUser);
}, [currentUserId, users]);

我怀疑 曾经 是否会找到匹配 user.id 与返回的参数对象进行比较。

解决方案

我怀疑 actual 路径参数真的是 userId。要么立即从 useParams 挂钩中解构它。

const { userId } = useParams();

或重命名currentUserIdparams并在钩子中解构userId

const params = useParams(props);

useEffect(() => {
  const { userId } = params;
  const selectedUser = users.find(user => user.id === userId);
  setSelectedUser(selectedUser);
}, [params.userId, users]);

关于路由参数和 id 属性的最后一点。路由参数将 always 是字符串类型。有时 id 实际上是一个数字类型,因此您需要使用 type-safe 相等性检查。 Convert ids to string types,涵盖了字符串id,将数字转换为number-like字符串。

useEffect(() => {
  const { userId } = params;
  const selectedUser = users.find(user => String(user.id) === userId);
  setSelectedUser(selectedUser);
}, [params.userId, users]);

防范 null/undefined 价值观。

  1. 首先,尽量保持稳定的selectedUser状态不变。换句话说,除非您有特定需要,否则不要将其更新为未定义的值。检查 users.find 的结果以查看是否找到匹配项。

    const { userId } = useParams(props);
    
    useEffect(() => {
      const selectedUser = users.find(user => String(user.id) === userId);
      if (selectedUser) {
        setSelectedUser(selectedUser);
      }
    }, [userId, users]);
    
  2. 其次,以防万一,防御性地防止渲染函数中的潜在 null/undefined 访问。您可以使用可选链接和空值合并运算符来保护空访问并为受控输入提供有效的定义值。

    <Input
      type='text'
      name='name'
      value={selectedUser?.name ?? ""}
      onChange={onChange}
      placeholder='Enter Name'
    />
    

    或者如果你喜欢 older-school null-checks/guard-clauses

    <Input
      type='text'
      name='name'
      value={(selectedUser && selectedUser.name) ?? ""}
      onChange={onChange}
      placeholder='Enter Name'
    />