从首页路由切换到搜索页,滚动页面时,List组件setState报错

来源:9-6 结果列表

郭小新

2018-06-16

从首页路由切换到搜索页,滚动页面时,List组件setState报错,可能是对已挂载的组件使用setState。

首页和搜索页都引用同一个List木偶组件,感觉像是List组件没有卸载就调用异步函数。

Bug:

//img.mukewang.com/szimg/5b247aba0001fe4711840410.jpg

后台数据也是混论的,在搜索页下拉List,首页的List也同时在向后台请求数据

//img.mukewang.com/szimg/5b247e490001303402780280.jpg 出现关键字那里已经切换到搜索页

代码:

Home(container)
class Home extends React.Component{
	constructor(props,context){
		super(props,context);
	}
	render(){
		return (
			<div>
				<HomeHeader cityName={this.props.userInfo.cityName} history={this.props.history} />
				<Banner />
				<div style={{height:'15px'}}>{/*分割线*/}</div>
				<Ad />
				<List cityName={this.props.userInfo.cityName} />
			</div>
		);
	}
	componentWillUnmount(){
		console.log('Home unmount');
	}
	componentDidMount(){
		console.log('Home did mount');
		this.changeCity();
	}
	changeCity(){
		// 从 localStorage 中获取城市
		let cityName = localStore.getItem(CITYNAME);
		if(cityName == null){
			cityName = '北京';
		}
		// 将城市信息存储到 redux 中
		this.props.userInfoActions.update({
			cityName: cityName
		});
	}
}

Home-List(container)
class List extends React.Component{
	constructor(props,context){
		super(props,context);
		this.state = {
			data: [],
			hasMore: false,//是否有下一页数据
			isLoadingMore: false,//是否正在 加载更多
			nextPage: 1//下一页
		};
	}
	render(){
		return (
			<div>
				<h2 className="home-list-title">猜你喜欢</h2>
				{
					this.state.data.length
					? <HomeList data={this.state.data} />
					: <div>加载中...</div>
				}
				{
					this.state.hasMore
					? <ListLoadMore isLoadingMore={this.state.isLoadingMore} loadMoreFn={this.loadListMore.bind(this)} />
					: ''
				}			
			</div>
		);
	}
	componentWillUnmount(){
		console.log('HomeList unmount');
		this._isMounted = false;
	}
	componentDidMount(){
		console.log('HomeList did mount');
		this._isMounted = true;
		this.loadListData();
	}
	//加载 List 数据
	loadListData(){
		const cityName = this.props.cityName;
		const promiseData = getListData(cityName,0);//返回 promise 对象
		this.handlePromise2Json(promiseData);
	}
	//处理加载的数据 promise对象->json
	handlePromise2Json(promiseData){
		promiseData.then(res => {
			return res.json();
		}).then(json => {
			const data = json.data;
			const hasMore = json.hasMore;
			if(this._isMounted){
				this.setState({
					data: this.state.data.concat(data),
					hasMore: hasMore
				});
			}	
		})
	}
	//加载更多 List 数据
	loadListMore(){
		//更新状态
		this.setState({
			isLoadingMore: true
		});
		//加载数据
		const cityName = this.props.cityName;
		const page = this.state.nextPage;
		const promiseData = getListData(cityName,page);
		this.handlePromise2Json(promiseData);
		//更新状态
		if(this._isMounted){
			this.setState({
				isLoadingMore:false,
				nextPage: this.state.nextPage + 1
			});
		}
	}
}

List(component)
class HomeList extends React.Component{
	constructor(props,context){
		super(props,context);
	}
	render(){
		return (
			<div id="home-list">
				{
					this.props.data.map((item,index) => {
						return <ListItem key={index} data={item} />
					})
				}
			</div>
		);
	}
	componentWillUnmount(){
		console.log('List unmount');
		console.log('---------------');
	}
	componentDidMount(){
		console.log('List did mount');
		console.log('---------------');
	}
}

ListLoadMore(component)
class ListLoadMore extends React.Component{
	constructor(props){
		super(props);
	}
	render(){
		return (
			<div className="list-load-more" ref='loadMore'>
				{
					this.props.isLoadingMore
					? <span>加载中...</span>
					: <span>加载更多</span>
				}
			</div>
		);
	}
	componentDidMount(){
		// 卸载监听
		window.removeEventListener('scroll',handleScroll);
		// 上拉时自动加载更多 list 内容
		let timeoutId;
		const isLoadingMore = this.props.isLoadingMore;
		const loadMoreFn = this.props.loadMoreFn;
		const loadMore = this.refs.loadMore;
		const callback = function(){
			// 获取 加载更多元素 与视窗顶部的距离
			const top = loadMore.getBoundingClientRect().top;
			// 获取可视窗口的高度
			const screenHeight = window.screen.height;
			if(top < screenHeight){
				loadMoreFn();
			}
		};
		// 处理 scroll 事件
		const handleScroll = function(){
			if(isLoadingMore){
				return;
			}
			if(timeoutId){
				//50 毫秒内存在该定时器则清空重置
				clearTimeout(timeoutId);
			}
			timeoutId = setTimeout(callback,50);
		}
		// 利用节流方式监听 scroll 事件,提高性能
		window.addEventListener('scroll',handleScroll);
	}
}

export default ListLoadMore;
写回答

2回答

双越

2018-06-19

看代码感觉没啥问题,你这样改改试一下。在 ListLoadMore 组件的 componentDidMount 函数体的最上部,加上一句:卸载掉 window 的 scroll 监听。

这样试一下,有问题再回复我。

0
1
郭小新
已修改,但还是这个BUG
2018-06-20
共1条回复

双越

2018-06-17

吧 ListLoadMore 的代码也贴出来看一下

0
1
郭小新
已粘贴
2018-06-18
共1条回复

React高级实战 打造大众点评Web App

已经对React Router4与Webpack2进行了项目升级

1711 学习 · 707 问题

查看课程