pmndrs / use-cannon

👋💣 physics based hooks for @react-three/fiber
https://cannon.pmnd.rs
2.76k stars 155 forks source link

Feature request: pass ref to useBody args callback #53

Closed RobRendell closed 4 years ago

RobRendell commented 4 years ago

Sorry to raise another feature request - and I'm not even sure if this is a valid idea.

use-cannon has been awesome, and my rolling die react component is working really well. The current class looks something like

function Die(props) {
   const geometry = useMemo(() => (buildGeometry(props.dieType)), [props.dieType]);
   const [ref] = useConvexPolyhedron(() => ({ args: geometry }));
   return (<mesh geometry={geometry} ref={ref} />);
}

Sometimes however I want to render the dice without physics, so I figured I might have one component which is just the mesh, and wrap it with another component which has the physics. Something like

function DieObject(props) {
   const geometry = useMemo(() => (buildGeometry(props.dieType)), [props.dieType]);
   return (<mesh geometry={geometry} ref={props.physicsRef} />);
}

function Die(props) {
   const [ref] = useConvexPolyhedron(() => ({ args: /* TODO */ }));
   return (<DieObject physicsRef={ref}  {...props} />);
}

As you can see, I don't have the geometry of the mesh available to initialise the body.

However, it occurs to me that the ref function gets the the mesh object, which has access to the geometry. If the ref was passed as a parameter to the args callback (after it has been initialised), then the Die component could be

function Die(props) {
   const [ref] = useConvexPolyhedron((ref) => ({ args: ref.current.geometry }));
   return (<DieObject physicsRef={ref} {...props} />);
}

Does this seem like it would be possible? Is it a good idea?

drcmda commented 4 years ago

i think you can access the ref, it's part of the local scope:

function Die(props) {
   const [ref] = useConvexPolyhedron(() => ({ args: ref.current.geometry }));

that callback is fired as a layoutEffect (view has rendered, but not committed to the screen or threejs in our case), so all refs are already wired up.

RobRendell commented 4 years ago

I initially tried to use the external ref as you suggest, but when I do that I get a run-time error:

Uncaught ReferenceError: Cannot access 'ref' before initialization

... which implies that the args callback is being invoked before useConvexPolyhedron returns.

drcmda commented 4 years ago

it shouldn't be. i looked it up and it happens because of forwardref which was added recently, it worked before that :-(

function useBody(type: BodyShapeType, fn: BodyFn, argFn: ArgFn, deps: any[] = []): Api {
  const { ref: fwdRef } = fn(0)

i would propose to remove dependencies in favour of an optional arg.

// now: dependencies serve no real function atm
// why would we ever want to reset a hook, it can be done by the api
const [ref] = useBox(() => ({ ref: forwardRef }), [uselessDependencies])

// proposal: local ref must be accessible, deps aren't needed
const [ref] = useBox(() => ({ args: ref.current.geometry }), forwardRef)
codynova commented 4 years ago

Resolved in #54, released in 0.2.11, closing