React/NextJS:如何映射键发生变化的对象数组?

问题描述 投票:0回答:2

我正在 NextJS 中开发一个项目,我发现了一个有趣的问题,但我似乎找不到解决方案 -

我有一系列从 Civic GAPI 获取的对象。 在这个阵列中,大多数对象是大选竞赛,但也有一些是全民公投。

contest
referendum
对象具有不同的结构。

这里是大选

contest
对象:

14:
candidates: (2) [{…}, {…}]
district: {name: "Thurston County", scope: "countywide", id: "ocd-division/country:us/state:wa/county:thurston"}
level: ["administrativeArea2"]
office: "Commissioner District 3"
roles: ["legislatorUpperBody"]
sources: [{…}]
type: "General"
__proto__: Object

这是一个

referendum
对象:

15:
district: {name: "Washington", scope: "statewide", id: "ocd-division/country:us/state:wa"}
referendumTitle: "Advisory Vote No. 8 (Senate Bill 6505)"
referendumUrl: "https://wei.sos.wa.gov/agency/osos/en/press_and_research/PreviousElections/2014/General-Election/Pages/Online-Voters-Guide.aspx"
sources: [{…}]
type: "Referendum"
__proto__: Object

这是我呈现此数据的页面(我已经包含了整个页面,但显然您可以跳过从 GAPI 加载数据的条件):

import useSWR from 'swr';

const Contests = (props) => {
  const url = '/api/voterInfo';
  const fetcher = (url) => fetch(url).then((r) => r.json());
  const { data, error } = useSWR(url, fetcher);

  if (error)
    return (
      <div className='alert alert-danger' role='alert'>
        <span className='sr-only'>Failed to load data!</span>
      </div>
    );

  if (!data)
    return (
      <div
        className='spinner-border spinner-border-lg text-danger'
        role='status'
        style={{ margin: '10rem', width: '20rem', height: '20rem' }}
      ></div>
    );

  if (data && data !== null) {
    const { contests } = data.data;

    return (
      <div>
        <table className='table table-hover'>
          <caption>List of contests in your area</caption>
          <thead style={{ fontFamily: 'Righteous, sans-serif' }}>
            <tr>
              <th scope='col'>#</th>
              <th scope='col'>Dist ID</th>
              <th scope='col'>Dist Name</th>
              <th scope='col'>Dist Scope</th>
              <th scope='col'>Office</th>
              <th scope='col'>Type</th>
            </tr>
          </thead>
          <tbody>
            {contests.map((contest, idx) => (
              <tr key={idx}>
                <th scope={idx}>{idx + 1}</th>

                <td>{contest.district.id}</td>
                <td>{contest.district.name}</td>
                <td>{contest.district.scope}</td>
                <td>{contest.office}</td>
                <td>{contest.type}</td>
              </tr>
            ))}
          </tbody>
        </table>
        {console.log(contests[0].level[0])}
      </div>
    );
  }
};

export default Contests;

如果我只渲染第一层键值对,很容易看出

contest
referendum
对象中的差异如何导致其中一个或另一个丢失值:

Missing values from referendumTitle, referendumUrl

因为我已将地图设置为从

contests
渲染键,所以它错过了
referendum
referendumTitle
键的
referendumUrl
值。

处理这个问题的最佳方法是什么? 我想出了两种可能的解决方案,但我不确定如何实施:

是否有一个条件可以设置为仅映射竞赛,并有一个单独的页面来映射公投? 我如何将它们(在映射数组之前/期间/之后)分开?

有没有一种方法可以动态映射键的值,而不管它们的标识符如何?

还有其他想法吗?

arrays reactjs dictionary next.js
2个回答
0
投票

有没有一种方法可以动态映射键的值,而不管它们的标识符如何?

你可以使用Object.keys:

Object.keys(contest).map(key => console.log(key))

0
投票

我最终使用

if
语句将 API 返回的数据分为 3 类,然后制作一个组件来单独加载每一类,因为它们受益于不同的格式

但这是我创建的用于调试返回数据的初始页面。它将数据分开,但将其全部显示在一页上,并根据返回的对象进行条件渲染:

import Link from 'next/link';
import useSWR from 'swr';

const Contests = (props) => {
  const url = '/api/voterInfo';
  const fetcher = (url) => fetch(url).then((r) => r.json());
  const { data, error } = useSWR(url, fetcher);

  if (error)
    return (
      <div className='alert alert-danger' role='alert'>
        <span className='sr-only'>Failed to load data!</span>
      </div>
    );

  if (!data)
    return (
      <div
        className='spinner-border spinner-border-lg text-danger'
        role='status'
        style={{ margin: '10rem', width: '20rem', height: '20rem' }}
      ></div>
    );

  if (data && data !== null) {
    const { contests } = data.data;

    const primaryElections = [];
    const generalElections = [];
    const referendums = [];
    contests.map((contest) => {
      if (contest.type === 'Referendum') {
        referendums.push(contest);
      } else if (contest.type === 'General') {
        generalElections.push(contest);
      } else if (contest.type === 'Primary') {
        primaryElections.push(contest);
      }
    });

    console.log(primaryElections, generalElections, referendums);

    return (
      <div>
        <table className='table table-hover'>
          <caption>List of contests in your area</caption>
          {primaryElections.length > 0 && (
            <>
              <thead style={{ fontFamily: 'Righteous, sans-serif' }}>
                <tr>
                  <th scope='col' style={{ width: '10px' }}>
                    #
                  </th>
                  <th scope='col'>Type</th>
                  <th scope='col'>Office</th>
                  <th scope='col'>Dist Name</th>
                  <th scope='col'>Dist ID</th>
                  <th scope='col'>Dist Scope</th>
                </tr>
              </thead>
              <tbody>
                {primaryElections.map((election, idx) => (
                  <tr key={idx}>
                    <td style={{ width: '10px' }}>{idx + 1}</td>
                    <td>{election.type}</td>
                    <td>{election.office}</td>
                    <td>{election.district.name}</td>
                    <td>{election.district.scope}</td>
                    <td>{election.district.id}</td>
                  </tr>
                ))}
              </tbody>
            </>
          )}
          {generalElections.length > 0 && (
            <>
              <thead style={{ fontFamily: 'Righteous, sans-serif' }}>
                <tr>
                  <th scope='col' style={{ width: '10px' }}>
                    #
                  </th>
                  <th scope='col'>Type</th>
                  <th scope='col'>Office</th>
                  <th scope='col'>Dist Name</th>
                  <th scope='col'>Dist ID</th>
                  <th scope='col'>Dist Scope</th>
                </tr>
              </thead>
              <tbody>
                {generalElections.map((election, idx) => (
                  <tr key={idx}>
                    <td style={{ width: '10px' }}>{idx + 1}</td>
                    <td>{election.type}</td>
                    <td>{election.office}</td>
                    <td>{election.district.name}</td>
                    <td>{election.district.scope}</td>
                    <td>{election.district.id}</td>
                  </tr>
                ))}
              </tbody>
            </>
          )}
          {referendums.length > 0 && (
            <>
              <thead style={{ fontFamily: 'Righteous, sans-serif' }}>
                <tr>
                  <th scope='col' style={{ width: '10px' }}>
                    #
                  </th>
                  <th scope='col'>Type</th>
                  <th scope='col'>Title</th>
                  <th scope='col'>Subtitle</th>
                  <th scope='col'>Language</th>
                  <th scope='col'>District</th>
                  <th scope='col'>Scope</th>
                  <th scope='col'>Open Civic Data ID</th>
                </tr>
              </thead>
              <tbody>
                {referendums.map((referendum, idx) => (
                  <tr key={idx}>
                    <td style={{ width: '10px' }}>{idx + 1}</td>
                    <td>{referendum.type}</td>
                    <td>{referendum.referendumTitle}</td>
                    <td>{referendum.referendumSubtitle}</td>
                    <td>
                      <Link href={`${referendum.referendumUrl}`}>
                        <a>{referendum.referendumUrl}</a>
                      </Link>
                    </td>
                    <td>{referendum.district.name}</td>
                    <td>{referendum.district.scope}</td>
                    <td>{referendum.district.id}</td>
                  </tr>
                ))}
              </tbody>
            </>
          )}
        </table>
      </div>
    );
  }
};

export default Contests;
© www.soinside.com 2019 - 2024. All rights reserved.