Skip to content

signals in react (three-fiber) doesn't work transiently #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
drcmda opened this issue Sep 9, 2022 · 2 comments
Open

signals in react (three-fiber) doesn't work transiently #115

drcmda opened this issue Sep 9, 2022 · 2 comments

Comments

@drcmda
Copy link

drcmda commented Sep 9, 2022

codesandbox: https://codesandbox.io/s/interesting-panna-urdtu6?file=/src/App.js

import { signal } from '@preact/signals-react'

const opacity = signal(1)

setTimeout(() => {
  // Should be transparent in 3 seconds ...
  // Works with opacity={opacity.value} but then it re-renders
  // Doesn't work as opacity={opacity}
  opacity.value = 0.5
}, 3000)

function Box() {
  return (
    <mesh>
      <boxGeometry />
      <meshBasicMaterial transparent color="orange" opacity={opacity} />
    </mesh>
  )
}

it's not a div, but it would be fantastic if it could work.

@developit
Copy link
Member

developit commented Sep 23, 2022

The reason this doesn't work is because we don't currently support fine-grained prop updates in the React integration. The Preact integration supports this because it injects fine-grained prop-to-DOM updates, but we cannot do that in React.

However, it is possible to implement VDOM-based fine-grained updates in a way that works nicely with react-three-fiber as well as React-DOM. Here is a patch that makes this work: (just import anywhere)

import { Signal } from '@preact/signals-react'
import * as rt from 'react/jsx-runtime'
import * as React from 'react'
rt.jsx = wrap(rt.jsx)
rt.jsxs = wrap(rt.jsxs)
let createElement = React.createElement
React.createElement = wrap(createElement)
const wrappers = new Map()
function wrap(method) {
  return function (type) {
    if (typeof type === 'string') {
      let t = wrappers.get(type)
      if (!t) wrappers.set(type, (t = Wrapper.bind(null, type)))
      arguments[0] = t
    }
    return method.apply(this, arguments)
  }
}
function Wrapper(type, props) {
  let p = {}
  for (let i in props) {
    let v = props[i]
    p[i] = i !== 'children' && v instanceof Signal ? v.value : v
  }
  return createElement(type, p)
}

Here it is working with the original demo code unmodified:
https://codesandbox.io/s/peaceful-brown-n1qjne?file=/src/App.js:91-121

@PHILIPP111007
Copy link

PHILIPP111007 commented Mar 2, 2025

async function editSubscribers(subscriber) {
        mainSets.value = {
            ...mainSets.value,
            invitationChanges: {
                friends: mainSets.value.invitationChanges.friends,
                subscribers: mainSets.value.invitationChanges.subscribers.map((user) => {
                    if (user.pk === subscriber.pk) {
                        return { ...user, isInRoom: !user.isInRoom }
                    }
                    return user
                })
            }
        }
    }

This function doesn't update mainSets.value.invitationChanges. subscribers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants