React-Views报错TypeError: 无法读取undefined的map属性问题
React setState后数据可控制台打印但map无法渲染的修复方案
问题现象
setState执行后可在回调中正常打印获取到的列表数据,但渲染阶段调用this.state.results.map()时报错,使用可选链this.state.results?.map()时页面无内容展示。项目使用express-react-views做服务端渲染,接口返回数据包含a、i两组对象列表。
原始问题代码
const Head = require ('../components/Head.jsx'); const Nav = require ('../components/Nav.jsx'); const Footer = require ('../components/Footer.jsx'); const getPipeLine = require('../../service/Contructor.js'); let display = new getPipeLine; class ActiveLoans extends React.Component { constructor(props) { super(props) this.state = { data: display }; }; async componentDidMount() { const activeDisplay = async () => await this.state.data.then((results) => { console.log('Data Fetched') !!!this.setState({results}); }); activeDisplay() }; render(){ return( <React.Fragment> <Head /> <Nav /> {this.state.results.map((result) => ( <div key={result.a.LoanNumber}> <thead> <tr> <th>Loan Number</th> <th>Loan Status</th> <th>Loan Type</th> <th>Submit Date</th> <th>Last Review</th> </tr> </thead> <tbody> <tr> <td>{result.a.LoanNumber}</td> <td>{result.a.LoanStatus}</td> <td>{result.a.LoanType}</td> <td>{result.a.SubmitDate}</td> <td>{result.a.LastReview}</td> </tr> </tbody> </div> ))} <Footer /> </React.Fragment> )}} module.exports = ActiveLoans;
已尝试的排查操作
- 在setState回调中打印数据,可正常输出获取结果,代码如下:
const activeDisplay = async () => await this.state.data.then((results) => { console.log('Data Fetched') !!!this.setState({results}, (async () => { let obj = await results; console.log('Done:', obj); })()); }); activeDisplay() };
- 尝试使用可选链写法调用map,页面无任何内容
接口返回数据结构示例
'0': { LoanNumber: 12345252, LoanStatus: 'Initial', LoanType: 'FHA', SubmitDate: '1/14/2022', LastReview: null }, '1': { LoanNumber: 12345626, LoanStatus: 'Initial', LoanType: 'VA', SubmitDate: '1/14/2022', LastReview: null }, '2': { LoanNumber: 12345670, LoanStatus: 'Initial', LoanType: 'Conventional', SubmitDate: '1/14/2022', LastReview: null },
根因分析
一共4个问题导致渲染失败:
- state初始值缺失:构造函数中仅初始化了
data字段,首次渲染(服务端渲染阶段不会执行componentDidMount)时this.state.results为undefined,直接调用.map()会触发类型错误,阻断渲染流程。 - 返回值为类数组对象:接口返回的是数字键名的类数组结构,本身不存在
map方法,即便setState执行成功也无法直接调用数组方法。 - 冗余语法干扰:
!!!this.setState()属于无意义写法,setState本身无返回值,三重非运算不会改变执行逻辑,但属于错误编码习惯。 - DOM结构不合法:
<thead>、<tbody>必须作为<table>标签的直接子元素,原代码将其放在循环生成的div中,不符合HTML规范,会导致表格渲染异常。
修复步骤
- 补全state初始值,给
results设置空数组默认值,避免首次渲染报错 - 移除setState前的冗余非运算,拿到接口返回值后先将类数组结构转为标准数组,再存入state
- 修正表格DOM结构,将thead移出循环,外层包裹合法table标签
- 增加数据加载态判断,数据未返回时展示加载提示
修复后完整代码
const Head = require ('../components/Head.jsx'); const Nav = require ('../components/Nav.jsx'); const Footer = require ('../components/Footer.jsx'); const getPipeLine = require('../../service/Contructor.js'); let display = new getPipeLine; class ActiveLoans extends React.Component { constructor(props) { super(props) this.state = { data: display, results: [] // 初始化results默认值 }; } async componentDidMount() { try { const rawResults = await this.state.data; // 类数组转标准数组,适配数组方法调用 const results = Array.from(Object.values(rawResults)); this.setState({results}); } catch (err) { console.error('贷款数据加载失败:', err); } } render(){ return( <React.Fragment> <Head /> <Nav /> {this.state.results.length === 0 ? ( <div>数据加载中...</div> ) : ( <table> <thead> <tr> <th>Loan Number</th> <th>Loan Status</th> <th>Loan Type</th> <th>Submit Date</th> <th>Last Review</th> </tr> </thead> <tbody> {this.state.results.map((result) => ( <tr key={result.a.LoanNumber}> <td>{result.a.LoanNumber}</td> <td>{result.a.LoanStatus}</td> <td>{result.a.LoanType}</td> <td>{result.a.SubmitDate}</td> <td>{result.a.LastReview}</td> </tr> ))} </tbody> </table> )} <Footer /> </React.Fragment> ) } } module.exports = ActiveLoans;
额外注意
express-react-views做服务端渲染时不会执行组件的componentDidMount生命周期,上述修复后首屏仍会先展示加载态,客户端hydrate后才会渲染列表。如果需要首屏直出列表数据,需要在服务端调用getPipeLine拿到数据后,通过props传入组件,不要在组件生命周期内获取首屏数据。
内容的提问来源于stack exchange,提问作者user123
相关产品推荐
相关产品推荐

