Published on

typescriptでuseRef使うときのメモ

Authors

ちょっとしたメモです

three.js を vite + typescript で使いたくて、最初につまづいたところ。

useRef の型

title=cameraのコンポーネント抜粋
import { OrbitControls, PerspectiveCamera } from "@react-three/drei"
import { useFrame } from "@react-three/fiber"
import { ReactNode, useEffect, useRef } from "react"

type P = {
  children: ReactNode
}

export const Camera:React.FC<P> =  ({children}) => {
    const orbitControlsRef = useRef<"ここに何いれていいか分からなかった!">(null)

    useFrame((state) => {
        if(orbitControlsRef.current){
            const {x, y} = state.mouse

            orbitControlsRef.current.setAzimuthalAngle(-x * angleToRadians(45))
            orbitControlsRef.current.setPolarAngle((y + 1) * angleToRadians(90 - 30))
            orbitControlsRef.current.update()
        }
    })

    return(
        <>
            <PerspectiveCamera makeDefault position={[0, 0, 10]} />
            <OrbitControls ref={orbitControlsRef} makeDefault />
            {children}
        </>
    )
}

useRef<>(null)の型を入れないと怒られちゃったけど、OrbitControls は何を指定したらいいか分からず、useRef<OrbitControls>(null)こんな感じで使ってました。
エラー出てても描画できてたし。。

でも気持ち悪いんで調べてたら、こんな書き方がありました。どこだったか忘れちゃった…

title=import部分とuseRef部分の抜粋
import { OrbitControls as OrbitControlsImpl } from "three-stdlib"

const orbitControlsRef = useRef<OrbitControlsImpl>(null)

確かにエラーでなくなったんだけど、 import { OrbitControls, PerspectiveCamera } from "@react-three/drei"ここでインポートしている OrbitControls との違いがいまいち分からない。
こういうモジュールの辿り方?を知っている方がいたら教えていただきたいです。

three.js は useRef を多用する感じなので、同じ問題にぶつかりそう…
command + クリックで辿っていく感じなのかなぁ。

おまけ

typescript をちゃんと勉強してないのが良くないんですが、こんなとこでも悩んでしまった。
Web Audio API を使って、constructor で音声入力の function をいくつか作りました。

title=mic.tsxの抜粋
class Mic {
    loaded: boolean;
    context: any;
    input: any;
    analyser: any;
    processor: any;
    spectrum: any;
    res: any;

    constructor() {
        this.loaded = false;
        navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
          this.context = new AudioContext();
          this.input = this.context.createMediaStreamSource(stream)
          this.analyser = this.context.createAnalyser()
          this.input.connect(this.analyser)
          this.analyser.fftSize = 2048;
          this.loaded = true;
        })
      }

        //これでオーディオ処理のストップと再開
        pause() {
                this.context.state === "running"
                ?
                this.context.suspend()
                :
                this.context.resume()
        }

        getLevel() {
            if (this.loaded) {
                //resultには周波数ごとの要素が1024格納される
            const result = new Uint8Array(this.analyser.frequencyBinCount)
            this.analyser.getByteFrequencyData(result)
            return result.reduce((a, b) => Math.max(a, b))
            }
            return 0
        }

        getFrequencyMulti(){
          if (this.loaded) {
            const result = new Uint8Array(this.analyser.frequencyBinCount)
            this.analyser.getByteFrequencyData(result)
            const encord =  Array.from(result)
            return encord
          }
          return []
          //最初return 0にしてたら戻り値の型がnumber[] | 0になってしまったので空の配列にした(自戒)
        }


        close() {
            this.context.close()
        }
}
export default Mic

getFrequencyMulti()のコメントにあるように、返り値を配列と number にしてしまっていた。
なので、返り値を受け取る所で型のエラーが出てしまっていた。
mic.getFrequencyMulti().lengthとかやっても「配列じゃなくて 0 の場合もあるのに length なんて使えないよ!」って怒られた(実行してくれてたけど)
ここらへんもちゃんと勉強しなきゃだけど、いつも使いながらなんですわん。

それではまた!