使用Taro开发小程序时候发现 ScrollView 会在同级节点发生增删情况下会自动会滚ScrollView到顶部,经过多次验证和查阅Taro原理发现这是Taro自身bug
出现回滚顶部bug的演示代码
下面有一个列表和按钮,点击按钮会出现加载更多的字样。但是当我们点击按钮时候,ScrollView就会惊奇发生会滚到顶部的现象。
const App: React.FC = () => {const [show, setShow] = useState<boolean>(false)const list = new Array(1000)const onClick = (): void => setShow(true)return (<View><View onClick={onClick} >按钮</View><ScrollView>{list.map(_, index) => (<View key={index}>{index}</View>)}</ScrollView>{show && <View>加载更多</View>}</View>)
}
出现BUG原因
因为Taro其实是将React所有组件的state更新归级Page统一进行管理即楼级组件对应的state是
// 初始化state
state = {Page: [ View, ScrollView ]
}
那么新增和删除节点就是
const oldPage = this.state.Page
this.setState({ Page: [ ...oldPage, View ]
})
这样微信进行diff时候认为是一个全新的数组,这样整个楼级组件都会重新创建。所以ScrollView会被重新创建,就会出现ScrollView自动会滚顶部的情况。
解决方案
- 避免同级节点操作,这个几乎很难。所以抛弃
- 使用 Block 组件包裹ScrollView的同级节点,那么我们更新节点时候就是单独对数组某个元素更新。这样就不会影响到ScrollView元素。
当我们用Block包裹 加载更多
发生更新的行为将是这样,那么在微信小程序diff对比时候只是楼层数组里面某个元素内容发生变化,就不会对ScrollView节点进行重建
const oldPage = this.state.Page
oldPage[2] = [ View ]
this.setState({ Page: oldPage })
解决bug代码
const App: React.FC = () => {const [show, setShow] = useState<boolean>(false)const list = new Array(1000)const onClick = (): void => setShow(true)return (<View><View onClick={onClick} >按钮</View><ScrollView>{list.map(_, index) => (<View key={index}>{index}</View>)}</ScrollView><Block>{show && <View>加载更多</View>}</Block></View>)
}