preactjs / preact

⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
https://preactjs.com
MIT License
36.35k stars 1.93k forks source link

Starting from version 10.20 will cause the order of child elements to be reversed #4405

Closed ShaoGongBra closed 3 weeks ago

ShaoGongBra commented 4 weeks ago

For example, if written like this, the actual rendering result will be text at the back and icons at the front 微信截图_20240607114524

Many places with judgment criteria will encounter similar problems

ShaoGongBra commented 4 weeks ago

The normal way of rendering is like this 微信截图_20240607114714

rschristian commented 4 weeks ago

As our issue template requests, please provide a reproduction. It allows us to help you all the quicker.

ShaoGongBra commented 4 weeks ago

As our issue template requests, please provide a reproduction. It allows us to help you all the quicker.

Because it is a pre act used on the WeChat mini program side in Taro, it is not easy to provide examples. In the webpage, I am not sure if such a problem will occur

rschristian commented 4 weeks ago

In the webpage, I am not sure if such a problem will occur

This is why we require a minimum reproduction though, if you're not sure it can even be replicated on a webpage, that makes our job quite a bit harder.

And 10.19.7 is fine? It's 10.20.0 that's the issue? That'd point the issue to #4312

ShaoGongBra commented 4 weeks ago

Yes, there is no problem with 10.19.7. I have tried versions up to 10.20.0 and later and have encountered this issue

在网页中,我不确定是否会出现这样的问题

这就是为什么我们需要最低限度的复制,但如果你不确定它是否可以在网页上复制,这会使我们的工作变得更加困难。

10.19.7 可以吗?这是 10.20.0 的问题所在吗?这会将问题指向#4312

JoviDeCroock commented 3 weeks ago

Hey!

I wrote #4312 and I would love to get to the bottom of this with you so there are a few things that we need to understand before we can give pointers for a minimal reproduction (PS: I highly encourage you to try creating one either way).

The changed code in 10.20.0 only triggers in the following scenario: We diff and no DOM node is produced by for instance returning null, then we see that the oldVNode that used to be in that position is equal to our last pointer, that means we need to leave the gap intact and move our pointer to the next VNode.

In the latest version this check has evolved to just checking whether our oldDom is present in the view.

I am doubtful whether this is the root-cause of this inconsistency mainly because it looks like you're saying it is wrong from the first render, or am I misunderstanding that? Nor are your returning null so that would also make me doubt whether this is the root-cause.

Do you have a runtime that is imitating the DOM? It might not implement the isConnected property which might make this check fire more than it needs to which might lead to this inconsistency? If you can't reproduce this in a browser the aforementioned case might be applicable here. All though in 10.20.0 we only rely on internal properties so you might need to do some digging here on your end to reproduce this.

My main goal here is that you can look at the scenario that triggers this check, i.e. a list shrinking by adding a null in a certain position, and extrapolating it to your case.

ShaoGongBra commented 3 weeks ago

What you said should be correct. It shouldn't be a problem that occurs during the first rendering because my icon is loaded asynchronously, so it will be re rendered after the icon is successfully loaded, and most of the problems seem to occur in the area with the icon,

My icon assembly is written as follows

import { Text } from '@tarojs/components'
import { useMemo } from 'react'
import { font, px } from '@/duxapp/utils'
import icons from './icons.json'
import './index.scss'

font.load('PlayerIcon', 'https://pictcdn.client.jujiang.me/fonts/PlayerIcon.1715861528075.ttf')

export const PlayerIcon = ({ name, color, size, style, className, ...props }) => {

  const _style = useMemo(() => {
    const sty = { ...style }
    if (color) {
      sty.color = color
    }
    if (size) {
      sty.fontSize = px(size)
    }
    return sty
  }, [color, size, style])

  const status = font.useFont('PlayerIcon')

  if (!icons[name]) {
    return console.log(`PlayerIcon的${name}图标不存在`)
  }

  if (!status) {
    return null
  }

  return <Text
    className={`PlayerIcon${className ? ' ' + className : ''}`}
    style={_style}
    {...props}
  >
    {String.fromCharCode(icons[name])}
  </Text>
}
ShaoGongBra commented 3 weeks ago

This icon is automatically rendered after successful loading, and in some cases, I change the state by clicking on the event, which can also cause the order of the child elements to be rearranged. The code looks like this

export const WeappTelLogin = ({
  onLogin,
  onCancel,
  onSkip
}) => {

  const [check, setCheck] = useState(false)

  const getPhoneNumber = useCallback(e => {
    if (e.detail.errMsg === 'getPhoneNumber:ok') {
      cmsUser.weappTelLogin(e.detail.code).then(data => {
        onLogin(data)
      })
    } else {
      onSkip()
    }
  }, [onLogin, onSkip])

  return <View className='cms-login-weapp--mask inset-0 absolute'>
    <View className='cms-login-weapp'>
      <View className='cms-login-weapp__head'>
        <CmsIcon name='guanbi1' size={36} color='#fff' />
        <Text className='cms-login-weapp__head__name'>登录</Text>
        <CmsIcon name='guanbi1' size={36} color={duxappTheme.textColor1} onClick={onCancel} />
      </View>
      {/* After clicking on the event, the Row component below will actually be rendered at this location */}
      {
        check ?
          <Button size='l' type='primary' openType='getPhoneNumber' className='button-clean' onGetPhoneNumber={getPhoneNumber}>快捷登录</Button> :
          <Button size='l' type='primary'
            onClick={() => confirm({
              content: '请阅读并勾选同意隐私政策',
              cancel: false,
              confirmText: '知道了'
            })}
          >快捷登录</Button>
      }
      <Text className='cms-login-weapp__tel' align='center' size={2} onClick={onSkip}>手机号登录</Text>

      {/* After clicking on the event, the Row here will be moved up two lines during actual rendering */}
      <Row items='center' className='gap-2 mt-3' justify='center'>
        {/* The Radio component includes an icon component */}
        <Radio checked={check} onClick={() => setCheck(!check)} />
        <Row>
          <Text>阅读并同意</Text>
          <Text type='danger' onClick={() => nav('duxcms/common/richtext?url=member/agreement&title=用户协议')}>《隐私政策》</Text>
        </Row>
      </Row>
    </View>
  </View>
}
JoviDeCroock commented 3 weeks ago

This is a custom react-reconciler though, not sure how that works with Preact 😅 I need a web reproduction, not how your components look, I'm sorry. I do notice that your ternary there doesn't have an else case so naively you can do check ? jsx : null which could be the issue here, as you also seem to have a case where you return void.

To re-iterate, if you don't go through the effort of making a reproduction I don't see how I can help you.

ShaoGongBra commented 3 weeks ago

I have created an example code. Please check this repository and run the yarn dev:weapp --app=test command after installing the dependencies. Then use the WeChat developer tool (download here: https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html )Open the dist/weapp directory for preview https://github.com/ShaoGongBra/preacttest

ShaoGongBra commented 3 weeks ago

code location home: src/test/pages/index/pages/home.jsx login: src/duxcmsUser/components/user/login.jsx line 426

ShaoGongBra commented 3 weeks ago

preact version src/duxapp/app.json line 48 Please modify the version here, otherwise it will not take effect. After the modification, restart the command yarn dev:weapp --app=test

rschristian commented 3 weeks ago

Then use the WeChat developer tool (download here: https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)

In case it wasn't clear, we need a web reproduction. The link you've provided is entirely in Chinese (which none of the core team speaks) and requires we set up a very niche environment just to try to reproduce your issue. This is asking far too much of those volunteering time to help, I hope you understand.

If you can reproduce this issue in a web environment we can take a look.

Open the dist/weapp directory for preview https://github.com/ShaoGongBra/preacttest

This is a massive repo, we'd need a minimal reproduction, i.e., the bare minimum code needed to showcase the issue. Please cut this down to just a few files.

ShaoGongBra commented 3 weeks ago

For this repository, you can only view the content of this file and the icon assembly. I cannot provide you with sample code in the web environment because I only used React in the WeChat environment to reduce the size of React packaging

https://github.com/ShaoGongBra/preacttest/blob/main/src/test/pages/index/pages/home.jsx

rschristian commented 3 weeks ago

For this repository, you can only view the content of this file and the icon assembly.

Please reduce the repository down to just this then.

I cannot provide you with sample code in the web environment because I only used React in the WeChat environment

It's not likely we'll be able to help in that case; this is a lot to ask of maintainers.

ShaoGongBra commented 3 weeks ago

This developer tool, you select the corresponding system download, install it,

  1. Please select tourist mode 2 Click on the plus sign
  2. Click on the icon on the right side of the directory and select the directory dist/weapp 4 Click OK 5 Completed

WX20240608-184204 WX20240608-184222 WX20240608-184237

JoviDeCroock commented 3 weeks ago

@ShaoGongBra I am reluctant to entertain this issue, frankly you are being disrespectful and you have no regard for what we tell you to do.

All of us work on Preact in our spare time and try to help everyone to the best of our ability. When we need to invest hours of that time into one persons issue that is very unfair to others. The issue here even has a custom reconciler and non standard tools.

All we ask is for a minimal way to reproduce this, on web, with a common tool like vite. You can use stackblitz/... I won't dig through multiple levels of libraries just to uncover that taro does not i.e. have a correct dom implementation/...

I went through the time to give you pointers as well as identify a mistake in your code, yet you throw more unrelated stuff at us.

rschristian commented 3 weeks ago

While we're always happy to see users utilizing Preact in non-standard/non-supported environments, unfortunately there's little we can do when you run into issues that cannot be reproduced in a web environment. There's simply far too many things that can go wrong that are outside the scope of Preact, and investing hours of time (for free) into niche tooling (and tooling that we'd have to constantly be using translation services just to work with) isn't fair to maintainers.

We'd love to help, but you need to do some work first to set up a minimal reproduction in an environment we actually support; WeChat and custom reconcilers are not supported.

As such, I'll close this out. If you do get a valid reproduction together then we can re-open and take another look.