Difficulty creating basic Line (@react-three/fiber and Typescript)

Viewed 2592

Can anyone help me get this line to show up? Im using @react-three/fiber and Typescript

My Faulty Component:


import * as THREE from 'three'

const RoadLine = ({start, end}: {start: THREE.Vector3, end: THREE.Vector3}) => {

    return (
        <line>
            <bufferGeometry setFromPoints={() => new THREE.BufferGeometry().setFromPoints([start, end])}/>
            <lineBasicMaterial color={'green'}/>
        </line>
    )
}

export default RoadLine


------------------------------------------------------------------------------------------

The Props Im sending in: 

{roads?.map(road => {
                        return (
                            <RoadLine
                            key={road.id}
                            start={new THREE.Vector3(1,0,3)} //numbers just for test atm
                            end={new THREE.Vector3(11,0,33)} //numbers just for test atm
                            />
                        )
                    })}

Im trying to make a basic line from point a to b essentially, not sure if I'm going about it correctly

2 Answers

I was struggling with this until I came across this response from Paul Henschel.

It becomes easier if you don't think of [line] as a wrapper or an abstraction. the jsx you write is threejs. if you write a line in threejs you can make it in jsx. but you're mixing up props and functions. THREE.BufferGeometry.setPoints is a function, and you overwrite it. in react/jsx does not call onClick, it sets it. you can't call a function declaratively, you do that as a side effect in either useEffect or useLayoutEffect. you use the latter if you want to call your function before the thing renders on screen.

He linked this codesandbox, I will post the code here as well. https://codesandbox.io/s/basic-demo-forked-e46t9?file=/src/App.js

import * as THREE from 'three'
import React, { useLayoutEffect, useRef } from 'react'
import { Canvas } from '@react-three/fiber'

function Line({ start, end }) {
  const ref = useRef()
  useLayoutEffect(() => {
    ref.current.geometry.setFromPoints([start, end].map((point) => new THREE.Vector3(...point)))
  }, [start, end])
  return (
    <line ref={ref}>
      <bufferGeometry />
      <lineBasicMaterial color="hotpink" />
    </line>
  )
}

export default function App() {
  return (
    <Canvas>
      <ambientLight intensity={0.5} />
      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
      <pointLight position={[-10, -10, -10]} />
      <Line start={[0, 0, 0]} end={[1, 0, 0]} />
      <Line start={[1, 0, 0]} end={[1, 1, 0]} />
    </Canvas>
  )
}

our friend's answer almost works, but for the typescript some tweaking is needed:

import * as THREE from 'three'
import React, { useRef } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'

type props = {
    start: number[],
    end: number[]
}

function Line (props: props) {
    const ref = useRef<THREE.Line>()

    useFrame(() => {
        if(ref.current){
            ref.current.geometry.setFromPoints([props.start, props.end].map((point) => new THREE.Vector3(...point)));
        }
    })
    return (
        <line ref={ref}>
            <bufferGeometry />
            <lineBasicMaterial color="hotpink"/>
        </line>
    )
}


export default function App() {
  return (
    <Canvas>
      <ambientLight intensity={0.5} />
      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
      <pointLight position={[-10, -10, -10]} />
      <Line start={[0,0,0]} end={[1,1,0]} />
      <Line start={[1,1,0]} end={[2,0,0]} />
    </Canvas>
  )
}
Related