News for hackers. Stuff that matters.
Re:React Renderer for Three.js by tills13 on Sunday August 15, 2021 @02:31PM (#28191134)
Is there an error in the examples? You have

    const mesh = useRef()

    ...

    <mesh ref={mesh} ...

You'll be rendering an undefined element (before the ref has a chance to attach).

Also, the TypeScript example makes me head hurt.

    const ref = useRef(null!)
Ah, yes. A non-null null literal.
Re:React Renderer for Three.js by sswu on Sunday August 15, 2021 @03:53PM (#28191935)
The TypeScript example does feel a little weird. I've used this library successfully with React + TypeScript with the following pattern:

  const mesh = useRef<THREE.Mesh>(null); // mesh has type RefObject<THREE.Mesh | null>
  ...
  useFrame((): void => {
    if (mesh.current != null) {
      mesh.current.rotation.x += 0.01;
    }
  });
  ...
  return <mesh ref={mesh} ... />

As others have mentioned, the ref will typically attach by the time a `useEffect` hook is called, but for safety it's nice to have the null check.
Re:React Renderer for Three.js by lxe on Sunday August 15, 2021 @08:43PM (#28193868)
I was scratching my head over this one too. Other answers in the thread don't seem right. I then realized this:

    const mesh = useRef(... 
assigns an instance of something (`React.mutableRefObject`) to a variable in scope, but

    <mesh ...
after compiling into normal JavaScript and becomes

    (0, _jsxRuntime.jsxs)("mesh"...
(see `console.log(__SANDBOX_DATA__.data.transpiledModules['/src/App.js:'].source.compiledCode) `)

So that's why you're not seeing the editor or runtime complain about this. `<mesh` (lowercase), as with other lowercased react elements get translated into `Something('mesh'`, and doesn't require a defined component variable.

Think this:

    const div = 'not a div -- i am a string'
    return <div>{div}</div>
Just one of the many gotchas of many layers of JavaScript/TypeScript/React/etc... programming environments.
Re:React Renderer for Three.js by strogonoff on Monday August 16, 2021 @02:01AM (#28195342)
I’d answer the original question…

> Is there an error in the examples? You have

    const mesh = useRef()
    ...
    <mesh ref={mesh} ...
> You'll be rendering an undefined element (before the ref has a chance to attach).

…as follows:

1. On that last line, <mesh> is just a regular element[0].

2. Ref assignment makes this element’s instance accessible within component code[1] under a constant, here coincidentally (and confusingly, I must say) named “mesh”. The constant could’ve been named anything else, like “el”, and the example would work exactly the same way:

    const el = useRef()
    ...
    <mesh ref={el} ...
3. How come the constant in the original example does not shadow the equivalently named built-in JSX element? Well, there is a convention/hard-coded rule in React/JSX/TSX where it simply would not resolve lowercase identifiers to custom components. If we change “mesh” to “Mesh” in the original example, then the app would attempt to use the ref constant as a (malformed) custom component, and predictably throw.

[0] https://www.w3.org/TR/2016/CR-SVG2-20160915/shapes.html#Mesh...

[1] https://reactjs.org/docs/refs-and-the-dom.html#refs-and-func...

Re:React Renderer for Three.js by dalmo3 on Monday August 16, 2021 @07:16AM (#28196843)
Excellent explanation, thank you!
Re:React Renderer for Three.js by mlsarecmg on Sunday August 15, 2021 @03:31PM (#28191714)
this is how refs work. it's the same with a div. the ref will be filled in useEffect, not before.

null! is a common typescript thing, it is a semantic guarantee that the ref is static and hence will be available. this saves you the if (ref.current) { ... } check.

Re:React Renderer for Three.js by tills13 on Sunday August 15, 2021 @03:46PM (#28191883)
To clarify, I'm not talking about the ref _prop_, I'm talking about them using the ref as the element.
Re:React Renderer for Three.js by mlajtos on Sunday August 15, 2021 @03:57PM (#28191971)
It’s lowercase, so it will act as plain-old tag. If the name would be uppercase, that would be a bug you described.
Re:React Renderer for Three.js by tills13 on Sunday August 15, 2021 @11:55PM (#28194799)
hm, interesting. I didn't know that -- I knew about custom tags / variable tags but never knew it _required_ them to be PascalCased. I feel like if I saw code like this come from one of my team members I'd ask them to change the name of the ref.
Re:React Renderer for Three.js by mlsarecmg on Sunday August 15, 2021 @03:59PM (#28191992)
no ref is used as an element in that code.

    const ref = useRef()
    useEffect(() => console.log(ref), [])
    return <div ref={ref} />
will return { current: [dom node] }

    const ref = useRef()
    useEffect(() => console.log(ref), [])
    return <mesh ref={ref} />
will return { current: [mesh node] }
Re:React Renderer for Three.js by tills13 on Sunday August 15, 2021 @03:38PM (#28191796)
The ref isn't used in an effect, though, at least not in the provided code.
Re:React Renderer for Three.js by onion2k on Sunday August 15, 2021 @04:57PM (#28192478)
The useRef hook doesn't return a ref. It returns a function for setting the ref value. The value is in ref.current. The ref prop calls the setter with a ref to the element on mount.
Re:React Renderer for Three.js by afloatboat on Sunday August 15, 2021 @02:53PM (#28191360)
I believe the `null!` is some trickery to let the rest of the code know that the `ref.current` will never be null, but with how React works you can’t really provide any other value. It prevents having to check for `null` everytime you address the value.

I don’t understand your first issue. The component is assigned to the ref, not the other way around, so why would it render `undefined`? It’s possible that `mesh.current` is undefined at first render, but that shouldn’t matter for the component, only for the reference.

Re:React Renderer for Three.js by tills13 on Sunday August 15, 2021 @03:43PM (#28191847)
Oops, I guess it's not undefined, but it'll attempt to render a ref object.

const mesh = useRef()

<mesh />

The underlying JavaScript (i.e. not JSX) on the first render , is React.createElement(mesh /* { current: undefined } */, { ref: mesh } )

In fact, I don't even think it matters which render you're talking about. It's still rendering a ref (again, { current: <undefined or a Three.MESH, I guess> }). That doesn't seem right to me.

Re:React Renderer for Three.js by petrbela on Sunday August 15, 2021 @08:03PM (#28193713)
Actually, the compiled JS is `React.createElement('mesh', { ref: mesh } )`. JSX compiler converts all lower-case names to strings. Only upper-case name (and composites like `rn.View`) are converted to variables.

The reason r3f uses lower-case names for built-in components is to distinguish them from your own components; from the documentation: "It merely expresses Three.js in JSX: <mesh /> becomes new THREE.Mesh(), and that happens dynamically." So whenever you see <mesh /> or anything else lower-case it is just an JSX alias for a Three.js core component. Note that they're not HTML tags in the resulting output; since they're rendered inside <Canvas />, they're transformed to Three.js automatically.

Re:React Renderer for Three.js by tills13 on Monday August 16, 2021 @12:06AM (#28194839)
Ah, right. It's very obvious in hindsight. Thank you for clarifying.
Re:React Renderer for Three.js by afloatboat on Sunday August 15, 2021 @05:54PM (#28192899)
Ah, right. But the mesh useRef is not wat is being rendered, the `mesh` component is more like rendering a div. If you open the sandbox and remove the useRef mesh and references it will still work.

Pretty confusing though, would have been better to call it `meshRef`. I also expected these components to be capitalised, but it probably makes sense in the way that in the dom renderer default components are lowercase as well.

Re:React Renderer for Three.js by tills13 on Monday August 16, 2021 @12:07AM (#28194841)
yeah imo simply changing the variable name of the ref would go a long way, here.
Re:React Renderer for Three.js by CraftThatBlock on Sunday August 15, 2021 @02:51PM (#28191342)
I'm guessing they are overloading the ref's usage with the custom renderer. I believe that a renderer can accept non-function/class components in theory, which is why this works.

I don't really like this though, although interesting, it feels like a hack and might not integrate well with tooling (e.g. the Typescript example).

How I would have implemented it was exposition a Mesh component which wraps this, and a useMesh hook to wrap and type the ref.

Re:React Renderer for Three.js by mlsarecmg on Sunday August 15, 2021 @03:41PM (#28191823)
r3f is a custom renderer, there is no difference in useRef between a div or a mesh, refs give you the underlying object. lowercase elements are native elements (div, span, mesh, view, box), they are defined by the renderer. uppercase is for components.
Re:React Renderer for Three.js by tills13 on Sunday August 15, 2021 @03:45PM (#28191866)
Ah, if they're using a custom JSX Pragma it might make sense. Still a little too bespoke for me.
Re:React Renderer for Three.js by mlsarecmg on Sunday August 15, 2021 @03:53PM (#28191937)
no special jsx pragma needed. this is actually just plain react, <div> <span> and so on come from react-dom, which defines these elements. other renderers define theirs. here's a mini custom renderer if this interests you: https://codesandbox.io/s/reurope-reconciler-hd16y
Re:React Renderer for Three.js by yashap on Sunday August 15, 2021 @03:26PM (#28191666)
Yeah, I don’t have experience with this lib, but the example code looks overly magic. But I’m also more of a backend dev, and find the useRef API to be inherently confusing, so that’s probably part of it.
Re:React Renderer for Three.js by shallowthought on Sunday August 15, 2021 @02:49PM (#28191322)
I have to wonder: Have we gone too far? Is TypeScript becoming more tedious than helpful? Has React lost its way in its blind zeal for pure functions?

...no, no, definitely not. Must've gone crazy for a second there.

Re:React Renderer for Three.js by tills13 on Sunday August 15, 2021 @03:46PM (#28191877)
I think I blame JavaScript for this mess, not the other way around.