Skip to content
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

Storing client in a useState triggers failing API request alongside other errors using 6.x #1029

Closed
eramdam opened this issue Jan 7, 2024 · 2 comments · Fixed by #1038
Closed
Labels
bug Something isn't working released

Comments

@eramdam
Copy link

eramdam commented Jan 7, 2024

I have an app that stores the result of createRestAPIClient (login in 5.x) in a React useState in order to then pass it through a React context. This all worked perfectly fine with masto.js 5.x. Unfortunately this seems to break with 6.x in weird ways I'm not sure I understand.

I have set up a repo with a minimal reproduction case to show the error
https://github.com/eramdam/masto.js-bug-repro

import { useEffect, useState } from "react";
import { createRestAPIClient, mastodon } from "masto";
import "./App.css";

function App() {
  const [restClient, setRestClient] = useState<
    mastodon.rest.Client | undefined
  >(undefined);

  useEffect(() => {
    const newRestClient = createRestAPIClient({
      url: "https://octodon.social",
      accessToken: "1234",
    })
    setRestClient(newRestClient)
  }, []);

  return <div>has Client {String(!!restClient)}</div>;
}

export default App;

Triggers the following errors (most interesting is the /api request that feels like shouldn't be happening)
image

@neet
Copy link
Owner

neet commented Jan 7, 2024

Hi @eramdam, sorry for the inconvenience. Starting from 6.x, we have introduced Proxy JavaScript API for the mastodon.Client object for reducing the bundle size.

Although it largely committed to lessening the size, the Client object itself no longer provides a clue for types or existence of its property at runtime. This means the following code can happen:

// index.js
import { createRestAPIClient } from "masto";

const client = createRestAPIClient({
  url: "https://mastodon.social",
});

console.log(typeof client);
console.log(typeof client.non.exitent.method);
$ node ./index.js
function
function

I suspect that the same thing is happening in this case. Since the second element in the return value of useState takes a function as well, the runtime code has no hints about whether the argument is a value or a closure.

const [count, setCount] = useState(0);

// Both usage are valid
setCount(1)
setCount(k => k+1)

const [client, setClient] = useState(undefined)
setCount(createRestAPIClient(...))
//       ^^^^^^^^^^^^^^^^^^^^^^^^^
// React has no idea whether this is a function or a value

As a workaround, you can avoid the error by turning it into a verbose closure call

const [client, setClient] = useState(undefined)
setCount(() => createRestAPIClient(...))
//               ^^^^^^^^^^^^^^^^^^^^^
// Wrap by a function that returns mastodon.Client

The current implementation with Proxy API is performant and robust, so there are currently no plans to get rid of it. However, it'd be better if we could provide more ergonomic experience with tools like React, at least provide more friendly error messages for unexpected calls. So please let me leave this issue open!

Copy link

🎉 This issue has been resolved in version 6.5.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working released
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants