pmndrs / react-three-flex

💪📦 Flexbox for react-three-fiber
MIT License
1.65k stars 42 forks source link

BREAKING: allow to pass an outer yoga provider #69

Closed saitonakamura closed 1 year ago

saitonakamura commented 2 years ago

Disclaimer: my original idea was to just replace yoga-layout-prebuilt with yoga-layout-wasm (like #67 or #37 ), but my experements proved importing wasm files and using yoga-layout-wasm in general is not easy task and requires configuring userland bundler, hurting the UX. For instance, I experienced cryptic webpack errors in storybook, vite tried to prebuild the wasm file instead of importing it as a url and so on. So I decided that if we can provide a way to pass a outer yoga runtime while still having the ability to use vanilla yoga-layout-prebuilt experience -- that's the right way

This is mainly to allow usage of yoga-layout-wasm. From now on there's 2 entrpoints, a default one with YogaPrebuiltProvider (which uses yoga-layout-prebuilt) and outerRuntime that exports YogaProvider that allows to pass an arbitrary runtime (that conforms with Yoga types)

This is a breaking change because library users will have to place a YogaProvider/YogaPrebuiltProvider in their code

Fixes #66

saitonakamura commented 2 years ago

I tried to come up with a way that doesn't require current users to mandatory use a provider if they are fine with yoga-layout-prebuilt. There's one, however it complicates the code too much IMO so I don't like it, but here it is

Instead of calling useYoga inside Flex and Box, allow to accept yoga as a prop Then on an entrypoint level provide wrappers that either import yoga-layout-prebuilt (vanilla) or get the value from the provider (wasm experience). Probably will look like

index.tsx

import { Flex: FlexImpl, FlexProps } from './Flex'
import Yoga from 'yoga-layout-prebuilt'

export const Flex = (props: FlexProps) => <FlexImpl yoga={Yoga} {...props} />

outerRuntime.tsx

import { Flex: FlexImpl, FlexProps } from './Flex'
import { useYoga } from './hooks'

export const Flex = (props: FlexProps) => {
  const Yoga = useYoga()
  return <FlexImpl yoga={Yoga} {...props} />
}

If you know another way to do this without breaking change, please speak up

saitonakamura commented 2 years ago

@astahmer wasm experience will work like this. Let me know if works for you

https://github.com/pmndrs/react-three-flex/pull/69/files#diff-dcf2f90bea62971aab7c1d7b44c7cb1ac0697e2ea9f52c134251ba20b68d370bR4-R13

import YogaWasmFile from 'yoga-layout-wasm/dist/yoga.wasm?url' // ?url this a vite thing to force file-loader

function App() {
  const initYoga = useCallback(() => Yoga.init(YogaWasmFile), [])

  return (
    <YogaProvider initYoga={initYoga}>
      {...}
    </YogaProvider>
  )
}
drcmda commented 2 years ago

will it making it easier if we host the yoga wasm (for instance yoga.pmnd.rs)? that's how useGLTF with draco currently works. the binary is hosted by google in that case and it's usually pre-cached by the browser. we do give users the ability to self host, in which case they'll have to put binaries into /public and the loader then merely xhr-fetches them.

it could look like this:

CDN WASM

import { Flex } from '@react-three/flex'

const Layout = () => (
  <Flex>

Self hosted

import { Flex } from '@react-three/flex'

const Layout = () => (
  <Flex binary="/yoga/index.wasm">

though i think "Flex" is just a generic component, haven't used it for longer. im also OK with a provider, though perhaps in that case as well Flex could function without any breaking changes if we default it to a CDN.

<Yoga binary="/index.wasm">
  <Flex>
    ...
</Yoga>

at least that's how threejs deals with wasm and i think it works quite well for them. imports would be also fine if bundlers can deal with them.

saitonakamura commented 2 years ago

It will, but only partly. It will help with settings up file-loader style of wasm file import (there was a problem with because it tries to precompile wasm files), for other bundles it's just a chore (but can be tricky if certain framework isn't flexible enough, create-react-app for instance).

But that's only one side of a coin, another side is that yoga-layout-wasm js glue tries to import path and fs for some reason, it's even somewhat mentioned in the docs, but trying to set it to false with webpack lead me to webpack complaining that it should be a string

Because of the latter issue I couldn't set yoga-layout-wasm up in neither of bundlers expect vite (I tried webpack and esbuild). And simultaneously, vite is the only one that can't use yoga-layout-prebuilt. I mean, it's basically a mess so that led me to idea that since there's no one-size-fits-all yoga js bindings, I'm essentially exposing a way to pass an outer runtime

I'll be trying to write my own wasm binding for yoga, maybe even without emscripten, but I'm pretty sure that's gonna take a long time so...