如何在 React 中返回 Object 的所有元素?
结论
不要先对 payments[0] 调用 Object.keys()。partialRepayments 已经是数组,可以直接调用 map()。在每条还款记录中,再遍历 paymentsFields,通过 item.key 读取对应字段。
{partialRepayments.map((payment, paymentIndex) => (
<div key={payment.orderId ?? paymentIndex}>
{paymentsFields.map(item => (
<div key={item.key}>
<span>{item.title}</span>
<span>{payment[item.key]}</span>
</div>
))}
</div>
))}
这样,每条部分还款记录都会生成一组字段,而不会只显示第一条。
原来的写法为什么无法正确展示
payments[0] 已经把数据范围限制在数组的第一项:
payments[0]
下面这段代码也没有真正遍历原数组:
[payments[0][val]].map(...)
方括号把 payments[0][val] 包装成了一个只有一个元素的新数组,因此 map() 只会执行一次。如果该值本身就是 partialRepayments 数组,那么回调中的 payment 仍然是整个数组,并不是单条还款记录。
还有一个问题是,箭头函数使用花括号后必须显式写出 return:
.map(val => {
return (...);
})
原代码的外层回调没有返回 JSX,也可能造成内容无法渲染。
推荐写法
假设接口结果保存在 loan 中,可以先安全地取出数组:
const partialRepayments = loan?.partialRepayments ?? [];
然后直接渲染:
{partialRepayments.map((payment: any, paymentIndex: number) => (
<section key={payment.orderId ?? paymentIndex}>
{repaymentsFields.map(item => (
<div key={item.key}>
<span>{item.title}</span>
<span>{payment[item.key] ?? '-'}</span>
</div>
))}
</section>
))}
如果贷款对象位于 payments 的第一项,数据路径可能是:
const partialRepayments = payments?.[0]?.partialRepayments ?? [];
完整示例如下:
const partialRepayments = payments?.[0]?.partialRepayments ?? [];
return (
<div>
{partialRepayments.length > 0 ? (
partialRepayments.map((payment: any, paymentIndex: number) => (
<div key={payment.orderId ?? paymentIndex}>
{repaymentsFields.map(item => (
<div key={item.key}>
<strong>{item.title}</strong>
<span>{payment[item.key] ?? '-'}</span>
</div>
))}
</div>
))
) : (
<div>暂无部分还款记录</div>
)}
</div>
);
这里优先使用 orderId 作为 React 的 key。只有接口没有提供稳定的唯一标识时,才使用数组索引。
字段配置必须与 JSON 一致
示例配置中有以下字段:
{
key: 'issuedDate',
title: lang.CLIENTS.REPAYMENTS.ISSUED_DATE,
}
但给出的 JSON 并不包含 issuedDate,只有 started、completed、timestamp 等字段。因此,下面的表达式会得到 undefined:
payment['issuedDate']
如果页面要显示还款开始时间,可以把配置改成:
export const repaymentsFields = [
{
key: 'started',
title: lang.CLIENTS.REPAYMENTS.ISSUED_DATE,
},
{
key: 'period',
title: lang.CLIENTS.REPAYMENTS.PERIOD_IN_DAYS,
},
];
也可以根据实际业务含义选择 completed 或 timestamp,但要先确认页面中的“日期”具体对应哪个字段。
显示嵌套对象中的金额
repaidPrincipalAmount 是一个对象,不能直接当作字符串或数字显示:
{
"amount": 250000.0
}
直接渲染下面的表达式会报错,因为 React 不能将普通对象作为子节点:
{payment.repaidPrincipalAmount}
可以让字段配置支持取值函数:
export const repaymentsFields = [
{
key: 'started',
title: lang.CLIENTS.REPAYMENTS.ISSUED_DATE,
},
{
key: 'period',
title: lang.CLIENTS.REPAYMENTS.PERIOD_IN_DAYS,
},
{
key: 'repaidPrincipalAmount',
title: lang.CLIENTS.REPAYMENTS.PRINCIPAL_AMOUNT,
getValue: (payment: any) => payment.repaidPrincipalAmount?.amount,
},
];
渲染时优先调用 getValue:
{partialRepayments.map((payment: any, paymentIndex: number) => (
<div key={payment.orderId ?? paymentIndex}>
{repaymentsFields.map(item => {
const value = item.getValue
? item.getValue(payment)
: payment[item.key];
return (
<div key={item.key}>
<span>{item.title}</span>
<span>{value ?? '-'}</span>
</div>
);
})}
</div>
))}
与在组件中逐个判断字段相比,这种方式更容易维护,也能用于日期格式化:
{
key: 'started',
title: lang.CLIENTS.REPAYMENTS.ISSUED_DATE,
getValue: (payment: any) =>
payment.started
? new Date(payment.started).toLocaleString()
: '-',
}
使用 TypeScript 定义数据类型
如果项目使用 TypeScript,尽量不要长期使用 any:
interface Amount {
amount: number;
}
interface PartialRepayment {
orderId: string;
repaidPrincipalAmount: Amount;
repaidInterestAmount: Amount;
repaidOverdueAmount: Amount;
repaidProlongationAmount: Amount;
started: string;
completed: string;
period: number;
timestamp: string;
}
组件中的遍历可以写成:
{partialRepayments.map((payment: PartialRepayment) => (
<div key={payment.orderId}>
<div>{payment.started}</div>
<div>{payment.period}</div>
<div>{payment.repaidPrincipalAmount.amount}</div>
</div>
))}
处理这类数据时,应先找到 partialRepayments 数组并直接调用 map(),再为每条记录遍历字段配置。不要用 payments[0] 代替整组还款记录,也不要用 [value] 把数据包装成单元素数组。
备注:内容仅供参考。
