无法读取 null 对象的属性(读取 “width”)在 Three.js React 组件中

huangapple go评论63阅读模式
英文:

Cannot read properties of null (reading "width") in Three.js React component

问题

我有一个Three.js应用程序,我遇到了一个问题。我有一些文件:<br><br>
main.js:<br>
它看起来完全像这个视频中的代码。我所做的一切就是将所有内容封装到一个函数中,以便我可以动态加载5个3D模型,而不是创建5个做同样事情的js文件。

还有React组件:

import { displayObject } from "../main"

export default function Atraction() {
  setTimeout(() => { displayObject(myArgs) }, 3000)
  return (
    <canvas className="webgl-1"></canvas>
  )
}

我收到了这个错误:

> 无法读取null的属性(读取"width")

它是指我正在尝试访问的canvas元素。我该怎么办?

英文:

I have a Three.js app and I have a problem. I have a few files: <br><br>
main.js: <br>
it looks exactly like the code from this video. All I did was to wrap everything into a function so I could load 5 3D models dinamically, and not create 5 js files that do the same.

And the React component:

import { displayObject } from &quot;../main&quot;

export default function Atraction() {
  setTimeout(() =&gt; { displayObject(myArgs) }, 3000)
  return (
    &lt;canvas className=&quot;webgl-1&quot;&gt;&lt;/canvas&gt;
  )
}

I get the error:

> Cannot read proprety of null(reading "width")

It refers to the canvas element that I am trying to access. What should I do?

答案1

得分: 1

不好意思,您的代码中包含一些HTML和特殊字符,我将只翻译其中的文本部分,不包括HTML和特殊字符。

"So, it is hard to figure out exactly how your code looks from the small fragment. But, what you have provided can probably be improved. What happens if the page takes longer to load than 3 seconds (slow internet for example)? setTimeout will break because the html hasn't been rendered yet. Also, do you really want people to have to wait 3 seconds for the objects to load if their computer loads it in 500ms?"

所以,从这小段代码很难准确了解您的代码是什么样的。但是,您提供的部分可能可以改进。如果页面加载时间超过3秒(例如,因为网络较慢),会发生什么情况?setTimeout会出错,因为HTML尚未呈现。而且,如果用户的计算机在500毫秒内加载完,您真的希望他们等待3秒才能加载对象吗?

"useEffect runs as soon as the DOM has rendered or whenever it reloads. It is safe to say we only want to establish the scene once, so we can use a guard at the start of a useEffect hook to make sure it only ever runs once (when the component first renders)."

"useEffect在DOM呈现后立即运行,或者在重新加载时运行。我们可以肯定,我们只想建立场景一次,所以我们可以在useEffect钩子的开头使用一个保护来确保它只在组件首次呈现时运行一次。"

"Additionally, with react functional components, we can use useRef to get a hook into the DOM. While technically we could use the id of the component to render our ThreeJS code, using a useRef() hook tells every programmer that we are going to be editing the DOM later -- it communicates intention which is helpful to other programmers to read."

此外,在React函数组件中,我们可以使用useRef来获取对DOM的引用。虽然从技术上讲,我们可以使用组件的ID来呈现我们的ThreeJS代码,但使用useRef()钩子告诉每个程序员,我们将稍后编辑DOM——这传达了意图,对其他程序员有所帮助。

"let rendered = false;"

"let rendered = false;"

"export function Component() {"

"export function Component() {"

"const ref = useRef() // this allows us to refer to the dom element once it has been created"

"const ref = useRef() // this allows us to refer to the dom element once it has been created"

"useeffect happens as soon as Component() has rendered to html"

"useEffect会在Component()呈现到HTML后立即发生"

"if (rendered) { // this makes sure the scene isn't created more than once even if Component() changes/re-renders"

"if (rendered) { // 这确保了即使Component()发生更改/重新呈现,场景也不会被创建多次"

"return;"

"return;"

"rendered = true;"

"rendered = true;"

"const scene = new THREE.Scene();"

"const scene = new THREE.Scene();"

"const renderer = new THREE.Renderer(... etc.);"

"const renderer = new THREE.Renderer(... 等等);"

"... setup scene/camera/renderer etc."

"... 设置场景/摄像头/渲染器等等。"

"... best to separate loading/adding-to-scene out of one function to avoid brittle code"

"...最好将加载/添加到场景的操作分开到不同的函数,以避免脆弱的代码"

"const car = loadObject('etc/car.gltf');"

"const car = loadObject('etc/car.gltf');"

"scene.add(car)"

"scene.add(car)"

"const house = loadObject('etc/house.gltf');"

"const house = loadObject('etc/house.gltf');"

"scene.add(house)"

"scene.add(house)"

"since the useEffect only runs once dom has rendered, ref will now refer to the dom"

"由于useEffect只在DOM呈现后运行一次,ref现在将引用DOM"

"ref.current.appendChild(renderer.domElement)"

"ref.current.appendChild(renderer.domElement)"

"return

"

"return

"

"function loadObject(objectLocation) {"

"function loadObject(objectLocation) {"

"load object at objectLocation"

"加载位于objectLocation的对象"

"return loadedObject"

"返回已加载的对象"

"Additionally, if my memory serves me correctly,"

此外,如果我记得正确的话,

"ref.current.appendChild(renderer.domElement)"

"ref.current.appendChild(renderer.domElement)"

"creates a new canvas component itself and puts it inside the target component. Therefore, the root component (as I have shown) is probably best to simply be a <div />. (citation needed)"

"它创建一个新的画布组件并将其放置在目标组件内。因此,根组件(如我所示)最好只是一个<div />。 (需要引用)"

"Since I haven't seen the rest of your code: if this doesn't help you, please post more code and I will update my answer to better aid you."

"由于我还没有看到您的其余代码:如果这没有帮助您,请发布更多代码,我将更新我的答案以更好地帮助您。"

英文:

So, it is hard to figure out exactly how your code looks from the small fragment. But, what you have provided can probably be improved. What happens if the page takes longer to load than 3 seconds (slow internet for example)? setTimeout will break because the html hasn't been rendered yet. Also, do you really want people to have to wait 3 seconds for the objects to load if their computer loads it in 500ms?

useEffect runs as soon as the DOM has rendered or whenever it reloads. It is safe to say we only want to establish the scene once, so we can use a guard at the start of a useEffect hook to make sure it only ever runs once (when the component first renders).

Additionally, with react functional components, we can use useRef to get a hook into the DOM. While technically we could use the id of the component to render our ThreeJS code, using a useRef() hook tells every programmer that we are going to be editing the DOM later -- it communicates intention which is helpful to other programmers to read.

let rendered = false;

export function Component() {
  const ref = useRef() // this allows us to refer to the dom element once it has been created

  // useeffect happens as soon as Component() has rendered to html
  useEffect(() =&gt; {
    if (rendered) { // this makes sure the scene isn&#39;t created more than once even if Component() changes/re-renders
      return;
    }
    rendered = true;

    const scene = new THREE.Scene();
    const renderer = new THREE.Renderer(... etc.);

    // ... setup scene/camera/renderer ect.

    // best to separate loading/adding-to-scene out of one function to avoid brittle code
    const car = loadObject(&#39;etc/car.gltf&#39;);
    scene.add(car)
    const house = loadObject(&#39;etc/house.gltf&#39;);
    scene.add(house)

    // since the useEffect only runs once dom has rendered, ref will now refer to the dom 
    ref.current.appendChild(renderer.domElement)

  });

  return &lt;div ref={ref} &gt;&lt;/div&gt;
}

function loadObject(objectLocation) {
  
   // load object at objectLocation

   return loadedObject
  
}

Additionally, if my memory serves me correctly,

    ref.current.appendChild(renderer.domElement)

creates a new canvas component itself and puts it inside the target component. Therefore, the root component (as I have shown) is probably best to simply to be a &lt;div /&gt;. (citation needed)

Since I haven't seen the rest of your code: if this doesn't help you, please post more code and I will update my answer to better aid you.

huangapple
  • 本文由 发表于 2023年3月9日 23:11:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75686494.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定