镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

useEffect + setState panics with RefCell already borrowed on macOS ARM64 #4

Closed

Description

@tomkennedy22

Hey! Humongous fan of perry!

Been playing around with perry-react today and hit a crash that I think is also responsible for the weather-app demo crashing on launch.
Calling a state setter inside useEffect panics every time:

function App() {
  const [status, setStatus] = useState("idle")

  useEffect(() => {
    setStatus("started")
  }, [])

  return <div><h1>Status: {status}</h1></div>
}
thread '<unnamed>' panicked at crates/perry-ui-macos/src/state.rs:382:13:
RefCell already borrowed

My guess is the useEffect fires and triggers a re-render while the initial render is still holding a borrow on the state RefCell.

I initially found this when cloning & testing the weather-app and it crashes on launch with the same abort — pretty sure it's the same root cause since it calls setState from inside a fetch callback on startup. I couldn't get weather-app working so started a new react repo and ultimately hit this error

macOS ARM64, built from source, perry-react from PerryTS/react.

Activity

  1. added a commit that references this issue on Mar 14, 2026
  2. proggeramlug commented on Mar 14, 2026

    @proggeramlug
    Contributor

    Hey Thomas, thanks for the detailed report — your diagnosis was spot on.

    Perry-react is admittedly the least tested part of our current UI surface, so really appreciate you putting it through its paces and taking the time to file this.

    Root cause: state_set() held an immutable borrow on the ON_CHANGE_CALLBACKS RefCell while iterating and invoking user closures via js_closure_call1. In perry-react, useEffect fires synchronously during the initial render — so when setStatus("started") triggers state_set → onChange callback → _scheduleRerender → component re-render → any new useState calls sig.onChange() → state_on_change() tries borrow_mut() on the same RefCell → panic.

    Fix: state_set() now snapshots the callback list into a local Vec before invoking any closures, releasing the RefCell borrow first. Same treatment applied to FOR_EACH_BINDINGS (which also calls user closures). Fixed across all 5 platform crates (macOS, iOS, GTK4, Windows, Android).

    Fixed in b521064.

  3. tomkennedy22 commented on Mar 14, 2026

    @tomkennedy22
    Author

    Awesome! Amazing response time, much appreciated. I'll test it out more on Saturday

  4. added a commit that references this issue on Apr 26, 2026
  5. added 2 commits that reference this issue on Jun 7, 2026
    3f65361
    eba26ac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions