问题出现在(已确认)
<a href="#" onClick={this.viewConfigFile.bind(this, file.url, file.path)}>
{file.path}
</a>
就是这个onClick事件找不到 viewConfigFile这个事件,报错 Cannot red property ‘addEventListener’ of null。
可问题是页面加载后,点击这个超链接是可以运行这个viewConfigFile的。那么,本人就猜想是 compoentWillReceiveProps更新页面的原理的问题。
在网上已经看过相关blog,可还是解释不出为什么会出现这个错误,求大神指导一二。
先简述页面的加载逻辑:
@1. 首先,ConfigFileListView这个组件加载时,会去后台申请加载文件数据。假设一开始,是请求url为’/stats/admin/configlist’的后台数据。
@2. 之后,由于父组件触发动作将redux的store中 state.router.location.pathnamem更改为‘/stats/admin/generatedconfiglist’。此时,store会通知ConfigFileListView的urlPath发了了变化,此时会将更新props,从而触发compoentWillReceiveProps。就在这时,浏览器打印出了错误 ‘addEventListener’ of null‘’ 。其他显示和操作一切正常,包括这个viewConfigFile事件也可以正常工作。
以上是,问题描述,下面给出代码:
import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getRouteKeyword} from '../../utils/SmallUtil';
import {getTscFileData, resetTscData, baseFetchDataForGet, resetDataInState} from '../../actions/configFile';
import {Table, Panel, Pagination, Modal, Button, PanelGroup} from 'react-bootstrap';
import {getShowItems} from '../../utils/SmallUtil';
// this component is for the "Uploaded Config File List" and "Generated Config File List" features
// of the column:"Config Files"
export class ConfigFileListView extends React.Component {
constructor(props) {
super(props);
this.state = {
activePage: 1,
showModal: false,
fileName: null,
activePanel: 0
};
}
componentWillMount() {
this.fetchData();
}
componentWillReceiveProps(nextProps) {
if(nextProps.urlPath != this.props.urlPath)
this.fetchData();
}
fetchData() {
let token = this.props.token;
let secret = this.props.secret;
let url = '';
let urlPath = this.props.urlPath;
if (urlPath == '/admin/config-files/uploaded') { //uploaded
url = '/stats/admin/configlist';
} else { //generated
url = '/stats/admin/generatedconfiglist';
}
this.props.baseFetchDataForGet(token, secret, url);
}
viewConfigFile(url, fileName) {
let token = this.props.token;
let secret = this.props.secret;
this.state.fileName = fileName;
this.props.getTscFileData(token, secret, url);
}
componentWillUnmount() {
this.props.resetDataInState();
}
render() {
let data = null;
if (this.props.data)
data = this.props.data.folders;
let folders = null;
if (data)
folders = getShowItems(data, 9, this.state.activePage);//show 9 items on one page;
let rightTitleName;
let currentUrl = this.props.urlPath;//"/admin/config-files/generated"
let viewChoice = getRouteKeyword(currentUrl);
if (viewChoice == 'uploaded') { //uploaded
rightTitleName = 'Config File List: Uploaded';
} else { //generated
rightTitleName = 'Config File List: Generated';
}
let sTscContent = this.props.sTscData;
if (sTscContent !== null) {
this.state.showModal = true;
}
return (
<div className="col-md-10">
<Panel header={rightTitleName} className="panel-success">
{
this.props.isFetching === true || folders ===null ? <h3>Loading data...</h3> :
<div>
<Pagination
ellipsis
boundaryLinks
bsSize="medium"
items={Math.ceil(data.length/9)}
maxButtons={8}
activePage={this.state.activePage}
onSelect={this.handlePageSelect.bind(this)} />
<PanelGroup activeKey={this.state.activePanel} onSelect={this.handlePanelSelect.bind(this)} accordion>
{
folders === undefined || folders.length === 0 ? '' : folders.map((folder, index1 = 0) =>
<Panel key={index1} eventKey={index1} collapsible header={<h3>{folder.name}</h3>} bsStyle="success">
<Table striped bordered condensed hover responsive>
<thead>
<tr>
<th>File Name</th>
<th>Related Products</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
{
folder.files.length == 0 ? '' : folder.files.map((file, index2 = 0) =>
<tr key={index2}>
<td>
<a href="#" onClick={this.viewConfigFile.bind(this, file.url, file.path)}>
{file.path}
</a>
</td>
<td>
<p>
{
file.relations.length == 0 ? <a>N/A</a> :
file.relations.map((relation, index3 = 0) =>
<a key={index3}>{' ' + relation.product + ' , '}</a>
)
}
</p>
</td>
<td>coming soon</td>
</tr>
)
}
</tbody>
</Table>
</Panel>
)
}
</PanelGroup>
</div>
}
);
}
}
const mapStateToProps = (state) => ({
data: state.configfile.data,
isFetching: state.configfile.isFetching,
sTscData: state.configfile.sTscData,
token: state.auth.token,
secret: state.auth.secret,
urlPath: state.router.location.pathname
});
function mapDispatchToProps(dispatch) {
return bindActionCreators({
baseFetchDataForGet,
resetDataInState
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(ConfigFileListView);