开发者问题收集

将对象的数组属性作为 React 组件 prop 传递会引发异常

2021-05-04
1271

我试图理解为什么我不能将数据对象的数组属性作为 prop 传递到组件中。认为这是一个关于 React 工作原理的基本理解问题,和/或在这种情况下的陷​​阱……任何见解都值得赞赏!

上下文:我正在为 React 中的会议制作一个展示页面。此展示页面将显示“title”、“description”等元素,并遍历“agenda_items”列表。

数据来自单个 JSON API 响应。“agenda_items”是数据结构中的嵌套数组。

{
  data: {
    id: 2,
    title: "Example title",
    description: "Example description",
    agenda_items: [
      {
        id: 5,
        title: "Topic 1"
      },
      {
        id: 6,
        title: "Topic 2"
      }
    ]
  }
}

以下实现有效。我正在使用 React.useState() 分别设置 meetingagenda_items ,尽管 agenda_items 理论上可以通过 meeting.agenda_items 访问。

const Meeting = () => {
  const [meeting, setMeeting] = React.useState([]);
  const [agenda_items, setAgendaItems] = React.useState([]);

  const handleFetchMeeting = React.useCallback(() => {
    axios
      .get(url)
      .then(result => {
        setMeeting(result.data);
        setAgendaItems(result.data.agenda_items);
      })
  });

  React.useEffect(() => {
    handleFetchMeeting();
  }, []);

  return (
    <div>
      <h1>{meeting.title}</h1>
      <AgendaList list={agenda_items} />
    </div>
  );
}

const AgendaList = ({ list }) => {
  return (
    <div>
      <h3>Agenda</h3>
      {list.map(item => (
        <Item 
          key={item.id}
          item={item}
        />
      ))}
    </div>
  );
}

但是,如果我尝试将 meeting.agenda_items 作为参数传递到 <AgendaList> ,它会引发异常。我原本以为这两种实现是等效的,但似乎不是?

...

return (
    <div>
      <h1>{meeting.title}</h1>
      <AgendaList list={meeting.agenda_items} />
    </div>
  );
)

//INSTEAD OF
/*
return (
    <div>
      <h1>{meeting.title}</h1>
      <AgendaList list={agenda_items} />
    </div>
  );
)
*/
Uncaught TypeError: Cannot read property 'map' of undefined
    at AgendaList (Meeting.jsx:52)
    at renderWithHooks (react-dom.development.js:14972)
    at mountIndeterminateComponent (react-dom.development.js:17734)
    at beginWork (react-dom.development.js:18935)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3922)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3971)
    at invokeGuardedCallback (react-dom.development.js:4031)
    at beginWork$1 (react-dom.development.js:23780)
    at performUnitOfWork (react-dom.development.js:22616)
    at workLoopSync (react-dom.development.js:22553)

为什么 React 在查找/渲染 {meeting.title} 时没有问题,但随后却调用 {meeting.agenda_items} 未定义?

这个错误是否发生在组件第一次渲染时,在异步数据调用完成之前?

2个回答

const AgendaList = ({ list }) => {
  return (
    <div>
      <h3>Agenda</h3>
      {list.map(item => (
        <Item 
          key={item.id}
          item={item}
        />
      ))}
    </div>
  );
}

更改为:

const AgendaList = ({ list }) => {
  return (
    <div>
      <h3>Agenda</h3>
      {list?.map(item => (
        <Item 
          key={item.id}
          item={item}
        />
      ))}
    </div>
  );
}

// if your code has support for optional chaining

或:

const AgendaList = ({ list }) => {
  return (
    <div>
      <h3>Agenda</h3>
      {list.length ? list.map(item => (
        <Item 
          key={item.id}
          item={item}
        />
      )) : <></>}
    </div>
  );
}

meeting.agenda_items 可能在呈现 <AgendaList ... /> 时不可用。因此,基本上发生的事情是,您正在映射当时可能不存在(在本例中为 undefined )的数据。由于数据准备就绪后值就会更新,并且组件会获得更新,所以这个新选项告诉组件只有在 list 有长度时才通过它进行映射,因为在这种情况下您需要一个对象(数组)。

list 尚未准备好时,您可以进一步添加加载器作为另一个选项,或者处理 list 实际上也为空的情况。

Tolumide
2021-05-04

首次渲染时,axios 请求尚未完成。此时,您已将默认值设置为数组。

{meeting.title} 将简单地评估为未定义,并在渲染中被忽略。

同样, meeting.agenda_items 也将未定义,因此 list.map() 将等同于 undefined.map() 并将引发错误。

当您仅传递 agenda_items 时,它已在 useState() 中定义为空数组,并且 list.map() 将等同于 [].map() ,这是有效的

charlietfl
2021-05-04