Taro 3.0.0-rc.0 中 自定义的 component 在 componentDidMount 中通过 Taro.createSelectorQuery.select 无法获取指定元素
interface IMarkdownProps {
className?: string
}
interface IMarkdownState {
isTooLong: boolean
isExpanded: boolean
}
class Markdown extends React.Component<IMarkdownProps, IMarkdownState> {
static defaultProps = {
type: 'html'
}
constructor (props) {
super(props)
this.state = {
isTooLong: false,
isExpanded: false,
}
}
componentDidMount () {
this.pageAdjust()
}
pageAdjust = () => {
const query = Taro.createSelectorQuery()
query.select('#wrapper').boundingClientRect((rect) => {
console.log('rect', rect)
if (rect) {
console.log('rect.height', rect.height)
this.setState({
isTooLong: rect.height >= 280,
});
}
}).exec()
}
render() {
const {className} = this.props
const {isTooLong, isExpanded} = this.state
return (
<View className={cn([s.root, isExpanded ? s.expanded : '', className])}>
<View id='wrapper'>
aaaaaaaa
</View>
{isTooLong && <View className={s.collapseButton} onClick={this.toggleExpanded}>
{isExpanded ? '收起描述' : '展开描述'}
</View>}
</View>
)
}
}
componentDidMount 中可获取到元素
无
Taro v3.0.0-rc.0
Taro CLI 3.0.0-rc.0 environment info:
System:
OS: macOS 10.15.5
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 12.16.3 - /usr/local/bin/node
Yarn: 1.16.0 - /usr/local/bin/yarn
npm: 6.14.4 - /usr/local/bin/npm
npmPackages:
@tarojs/components: 3.0.0-rc.0 => 3.0.0-rc.0
@tarojs/taro: 3.0.0-rc.0 => 3.0.0-rc.0
@tarojs/webpack-runner: 3.0.0-rc.0 => 3.0.0-rc.0
eslint-config-taro: 3.0.0-rc.0 => 3.0.0-rc.0
nervjs: ^1.5.6 => 1.5.6
react: ^16.10.0 => 16.13.1
taro-ui: 3.0.0-alpha.1 => 3.0.0-alpha.
正常情况下, 你可以监听页面的 onReady 事件,在元素渲染后再查询。
但是,@yuche rc2 版本 并没有执行。
Taro.eventCenter.once(Taro.Current.router.onReady, () => {
const query = Taro.createSelectorQuery()
query.select(this.viewId).boundingClientRect()
query.exec(res => {
console.log(res, 'res')
})
console.log('onReady')
})
@Chen-jj rc2 版本 Taro.eventCenter.once(Taro.Current.router.onReady, () => {}) 不会执行,请抽空看一下。
componentDidMount 代表 React 组件组装完毕交给 Taro 处理, Taro 刷新到 UI 是有延迟的, 就像 createSelectorQuery 一样的延迟. 所以 componentDidMount 时节点还不存在.
解决办法有3种:
<View id='wrapper' ref={node => { Taro.createSelectorQuery().select(`#${node?.id}`) }}>如果 1 和 2 都不适合, 可以试试 3, 但要考虑去重和防抖.
@cncolder 刚刚使用 3 试了一下,ref 只能拿到组件,并不意味着页面组件dom 已经渲染完成。
@cncolder 试了一下 ,还是不行
receiveRef = node => {
if (!node) return;
this.viewRef = node
const query = Taro.createSelectorQuery()
query.select('#' + this.viewId).boundingClientRect()
query.exec(res => {
console.log('未延迟', res)
})
setTimeout(() => {
const query = Taro.createSelectorQuery()
query.select('#' + this.viewId).boundingClientRect()
query.exec(res => {
console.log('延时500毫秒', res)
})
console.log('onReady')
}, 500);
}
未延迟 [null]
延时500毫秒 [{…}]
@yuche 我是在3 4层深的子组件中,只能使用 eventCenter 来使用 onReady ,参考的 #6221
发现个很奇怪的问题
componentDidMount 中直接执行下面这段 就可以 ok
Taro.eventCenter.once(Taro.Current.router?.onReady, () => {
console.log('[ok]')
})
但是如果加了判断,就不会执行ok,更离奇的是 除了 onLayout 别的props字段都没事,onLayout 是 function 类型
if (this.props.onLayout) {
Taro.eventCenter.once(Taro.Current.router?.onReady, () => {
console.log('[ok]')
})
}
@cncolder 真的不行,您看看 我试了 beta6 rc0 rc2 都不行
<View
ref={node => node && Taro.createSelectorQuery()
.select(`#${node.id}`)
.boundingClientRect()
.exec(console.log)
}
onClick={onClick}
className={className} style={style} {...other}
>
{children}
</View>

@yuche 我是在3 4层深的子组件中,只能使用 eventCenter 来使用 onReady ,参考的 #6221
发现个很奇怪的问题
componentDidMount 中直接执行下面这段 就可以 okTaro.eventCenter.once(Taro.Current.router?.onReady, () => { console.log('[ok]') })但是如果加了判断,就不会执行ok,更离奇的是 除了 onLayout 别的props字段都没事,onLayout 是 function 类型
if (this.props.onLayout) { Taro.eventCenter.once(Taro.Current.router?.onReady, () => { console.log('[ok]') }) }
看起来你的方法能解决我的问题
@charmtiger 你的 View 没有 id, onLayout 是你的业务代码, 解决问题之前要排除业务代码的干扰.
这里不是论坛.
@yuche 我已经暂时得出结论,当组件是根据接口参数,来判断是否实例化,也就是说当页面 onReady 的时候,这个组件并没有存在于组件树中,等接口返回后,再实例化组件,在组件的 componentDidMount 中直接调用 query 和使用 eventCenter 都无法查找到元素和执行。另外我对 cncolder 的 ref 的方式,在上述的情况中,同样无法通过多次执行 ref 来拿到元素,只会执行一次。
我认为目前 eventCenter 的方式,只是利用小程序页面的 onReady,对于后来出现的dom是不适用的。理想情况是每次渲染过后,都会有一次类似 Vue.nextTick 的回调,不过感觉不容易实现。
我知道这里不是论坛,我也在大量的尝试各种可能,尽可能确认问题,如果啰嗦了很抱歉🤗。
--------- 更新
刚刚发现 Taro 提供了 Taro.nextTick,使用后问题解决,另外请问 Taro.nextTick 能否代替 eventCenter 的方式?
Taro 2.1.5, 本地调试正常可以通过createSelectorQuery准确获取到指定内置组件的高度,并成功渲染;但真机调试获取到的组件高度就不准确了。any suggestions? @charmtiger
@SamChangi 我看 taroui 也是用 延时 来 query的,我也是改用 如果查不到元素,就延时 反复查。
@SamChangi 我看 taroui 也是用 延时 来 query的,我也是改用 如果查不到元素,就延时 反复查。
fixed, thanks a lot
这个先打开,后续我们再找有没有更好的方案
之前taro2版本组件内是要加 .in(this.$scope) 的,后来发现taro3去掉 .in(this.$scope) 就好了
const query = Taro.createSelectorQuery().in(this.$scope)
改为
const query = Taro.createSelectorQuery()
(实测环境):"@tarojs/taro": "3.0.0-rc.3",
试试?
`//查询dom的宽度和高度,注意这里获得的是一个px单位
async function getDOMRect(id){
const query = Taro.createSelectorQuery();
query.select('#'+id).boundingClientRect()
return new Promise((reslove,reject)=>{
query.exec(res=>{
if(res[0]==null){return reject('Cannot find the #'+id+' selector')}
reslove({
width:res[0].width,
height:res[0].height,
})
})
})
}
//不断查询直到有结果
function poll(promiseFunc){
return promiseFunc().catch(()=>new Promise(reslove=>Taro.nextTick(()=>reslove(poll(promiseFunc)))))
}
调用的时候:
poll(‘id’).then(console.log)
`
在 #6776 之后,nextTick 函数一定会在 onReady 之后运行
Most helpful comment
在 #6776 之后,nextTick 函数一定会在 onReady 之后运行