Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@jxom
Copy link
Member

@jxom jxom commented Feb 9, 2022

@changeset-bot
Copy link

changeset-bot bot commented Feb 9, 2022

⚠️ No Changeset found

Latest commit: 6bf75f1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Feb 9, 2022

Someone is attempting to deploy a commit to a Personal Account owned by @tmm on Vercel.

@tmm first needs to authorize it.

@sammdec
Copy link
Contributor

sammdec commented Feb 9, 2022

This looks great to me! Amazing work!

@shunkakinoki
Copy link
Contributor

shunkakinoki commented Feb 9, 2022

@jxom

Thank you for your rfc!

As someone who is using swr, it would be great if we can consider both react-query & swr caching approaches as listed here, having different package entrypoints and being peer dependency agnostic

One of the many strong points about this library is that its only dependency is ethers, and this leads to a very fast and smooth, while being composible lib

ref:

@gosseti
Copy link
Contributor

gosseti commented Feb 9, 2022

This proposal looks fantastic, great work!

@vercel
Copy link

vercel bot commented Feb 9, 2022

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/zoo/wagmi/HzcdRtWq1SUwncuomxPg54MZDmdG
✅ Preview: https://wagmi-git-fork-jxom-rfc-deterministic-states-caching-zoo.vercel.app

Copy link
Member

@tmm tmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks great! Some details to chat about.

Consistent Defaults

Better to have opinionated defaults for specific hooks (i.e. useEnsAvatar default enabled, useFeeData default disabled, etc.) versus the same defaults?

Think it makes sense that “we” (the library authors) use our industry knowledge to help assist folks around cache duration/location, etc., but would it be too astonishing if defaults aren't consistent?

Testing

How do we want to handle tests for the increased surface API? Do we test config arguments that are React Query config? Probably doesn’t make sense to test all the options that are just passed through to React Query, but maybe some.

Dependency Type

Should React Query be a core or peer dependency? ethers.js is a peer dependency. Could see a case for either approach.

Additional Configuration

Moving forward, how do we decide what React Query features make it into wagmi or not? Should we support additional hook-level configuration (i.e. retry, refetchOnWindowFocus, etc)?

Do we want to expose query keys (or functions for getting array keys)? Definitely more pro, but could help folks tune things to their liking


## A note on hook naming

I feel like the naming of read hooks such as `useAccount`, `useBalance`, `useNetwork` are fine, however I reckon it would be nice to be more explicit with action hook naming to include intending verb (i.e. `useTransactionSend` instead of `useTransaction`). This also aligns with existing hooks such as `useContractWrite` and `useEnsLookup`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree about the naming change for useTransaction (and action hooks in general)

})
```

I feel like if we flip the logic, and introduce an `enabled` argument, this would make DX a bit easier to follow & reason about:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100% - thought about doing this before and makes a lot more sense.

- `isReloading`: A flag to indicate that the data is loading in the background, and showing cached success/error data in the meantime (meaining this flag can be truthy when isSuccess/isError is truthy).
- `isSuccess`: A flag to indicate the data has been fetched successfully.
- `isError`: A flag to indicate that an error was thrown upon fetching the account

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also expose status too? Think it's a useful prop for folks that don't like booleans.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, great idea.

@tmm tmm force-pushed the main branch 4 times, most recently from 49eadcb to 4ce2969 Compare February 10, 2022 02:08
@jxom
Copy link
Member Author

jxom commented Feb 10, 2022

Consistent Defaults

Better to have opinionated defaults for specific hooks (i.e. useEnsAvatar default enabled, useFeeData default disabled, etc.) versus the same defaults?

Think it makes sense that “we” (the library authors) use our industry knowledge to help assist folks around cache duration/location, etc., but would it be too astonishing if defaults aren't consistent?

Yeah absolutely. I guess the objective of this PR is to essentially make the UX of wallet & contract interaction better, and to achieve this, we need to enforce opinionated defaults. However, it could be a little confusing from a DX perspective if we have inconsistent defaults (some hooks are cached, while some are not), I guess there are a couple of alternatives at the top of my mind:

  1. Disable the cache by default, and make it opt-in on a per-hook basis (a user can set a cacheEnabled flag or something to enable the cache),
  2. Just leave it as is, per RFC spec, to push & assist developers with opinionated defaults, but clearly state in the docs on a per-hook basis which ones are cached, and which ones are not.

Testing

How do we want to handle tests for the increased surface API? Do we test config arguments that are React Query config? Probably doesn’t make sense to test all the options that are just passed through to React Query, but maybe some.

Yeah true, maybe we could just test the more "critical" functionality like the new state vars & ensuring caching works as intended?

Dependency Type

Should React Query be a core or peer dependency? ethers.js is a peer dependency. Could see a case for either approach.

If ethers is a peer dep, then I think it might make sense for react-query to also be a peer dep. Though, my only concern, is that there could be users who consume an alternative async state library (such as SWR), so it might be weird having both deps in their project. In that case, it might make sense to have it as a core dependency to hide the react-query surface entirely from them.

Additional Configuration

Moving forward, how do we decide what React Query features make it into wagmi or not? Should we support additional hook-level configuration (i.e. retry, refetchOnWindowFocus, etc)?

Yeah, definitely see an advantage in exposing more React Query features, but didn't want the RFC to feel too overwhelming. Maybe we could extend the config arg to accept RQ config that make sense for this library?

Do we want to expose query keys (or functions for getting array keys)? Definitely more pro, but could help folks tune things to their liking

Yeah, totally, I think it would be beneficial if consumers want to programmatically interact with the react-query query client. However, I feel like we should blanket the query client with our own API, as the RQ terminology might not align with this library, might be overwhelming for users to learn RQ, and we might want to constrain the API. For example, we might want to have query client functionality more specific to our "read" hooks:

  • queryClient.getQueryData -> wagmiClient.getData(useAccount), wagmiClient.getData(useBalance, { addressOrName: '...' }), etc
  • queryClient.prefetchQuery -> wagmiClient.prefetch(useAccount), wagmiClient.prefetch(useBalance, { addressOrName: '...' }), etc
  • queryClient.refetchQueries -> wagmiClient.refetch(), wagmiClient.refetch(useAccount), wagmiClient.refetch(useBalance, { addressOrName: '...' }), etc
  • etc etc for other queryClient stuff

Not sure if that should be in-scope for an MVP of this? Or should I extend this RFC to touch the surface of this?

@jxom
Copy link
Member Author

jxom commented Feb 10, 2022

@jxom

Thank you for your rfc!

As someone who is using swr, it would be great if we can consider both react-query & swr caching approaches as listed here, having different package entrypoints and being peer dependency agnostic

One of the many strong points about this library is that its only dependency is ethers, and this leads to a very fast and smooth, while being composible lib

ref:

Yeah, the alternative of having separate entrypoints has definitely been a valid consideration! But I think the downsides of this approach outweighed the upsides. We need to keep in mind that some users wouldn't be using an async state lib anyway, so having the option of picking between using react-query or swr could be a bit overwhelming. IMO, I think it would be less overwhelming to the end user (developer) if they don't have to make this choice, and it's already built in. Plus, having it built in means we can change up the API terminology a bit to make more sense for wagmi (so the consumer doesn't have to worry about learning an additional RQ or SWR API) & also enforce a constrained/minimal API surface.

@sammdec
Copy link
Contributor

sammdec commented Feb 10, 2022

My 2 cents re: Consistent Defaults

  • I would rather go the more opinionated route but we are providing a set of consistent configurations for users who want to change, so while the defaults are not consistent the api to change them is.

@tmm
Copy link
Member

tmm commented Feb 10, 2022

Re: Consistent Defaults

Let's move forward with them. As long as they are clear in the docs, these can be a helpful assist. Can see how folks are tuning these once it's released and can adapt accordingly too.

Re: Testing

Testing state vars and that caching is working as intended seems appropriate.

Re: Dependency Type

Agree with the trade-offs. Let's make it a core dependency.

Re: Additional Configuration

Agree, can start with the basics and see what makes sense later. Can leave an advanced parts (i.e. exposing query keys) out of scope for now.

Re: Multiple Entrypoints

Definitely think multiple entrypoints for React Query, SWR, etc. would have been cool, but not sure how realistic it is to build and maintain.

Right now, it seems like React Query has more energy around it from wagmi contributors. That said, keeping things lightweight resonates with me (why I suggested entrypoints) and I've also been in situations before where I was frustrated with decisions maintainers made for OSS projects.

I'm not sure we can make everyone happy with this individual decision, but whatever direction we go is a step towards making wagmi the best React library for Ethereum.

@shunkakinoki really appreciate your feedback and happy to chat about this further async or live if you want.

@tmm
Copy link
Member

tmm commented Feb 10, 2022

Going to give the weekend for folks to share additional thoughts :)

In the meantime, we can start chatting about implementation.* We'll need to update the hooks, tests, docs, and examples. I'm down to work on this and would be great if there were one or two others that were also interested. Could make sense for folks to work on specific hooks and update them across surfaces. As part of this work, I'm also down to expand the core team and give folks access to the repo.

Anyone interested in being involved in implementation? What do we think about assigning hooks to work on versus doing things more horizontally across surfaces?


*We probably also want to figure out the best way to message changes, but can do that later.

@sammdec
Copy link
Contributor

sammdec commented Feb 10, 2022

@tmm I can give some hours to the project to help out with implementation, doc updates etc. Wont be available next week but I can start from the week after.

@jxom
Copy link
Member Author

jxom commented Feb 10, 2022

Going to give the weekend for folks to share additional thoughts :)

In the meantime, we can start chatting about implementation.* We'll need to update the hooks, tests, docs, and examples. I'm down to work on this and would be great if there were one or two others that were also interested. Could make sense for folks to work on specific hooks and update them across surfaces. As part of this work, I'm also down to expand the core team and give folks access to the repo.

Anyone interested in being involved in implementation? What do we think about assigning hooks to work on versus doing things more horizontally across surfaces?

*We probably also want to figure out the best way to message changes, but can do that later.

Yeah, I'm defs down to help out with this.

@shunkakinoki
Copy link
Contributor

shunkakinoki commented Feb 10, 2022

I'm also def down to help!

Also would love for the opportunity to chat and contribute regarding this.

cc: thank you to @jxom for the wonderful rfc!

@shunkakinoki
Copy link
Contributor

shunkakinoki commented Feb 10, 2022

That said, keeping things lightweight resonates with me (why I suggested entrypoints) and I've also been in situations before where I was frustrated with decisions maintainers made for OSS projects.

Speaking of, i do still stand by the above

As of the moment (2/10/2022), npm downloads of react query vs swr ration is roughly about 2x
1,120,187 vs 591,196
However, the package size diff is about 10x
2.25 MB vs 228 kb

As a swr enthusiast, it would be great if we can move forward wagmi to having multiple entrypoints and being peer dependency agnositic, despite its technical challenges - love to be of help too.

@bpierre
Copy link
Contributor

bpierre commented Feb 10, 2022

Looking really good! It all makes sense and will make the API more consistent. I think the best thing about React Query is the way it introduces a standard structured object to represent data fetching, making it possible to compose functions / hooks together, as promises do already for simple async operations.

Regarding using react-query vs. swr, I checked some data about the size of the library:

As said in the RFC, react-query will add ~13kb to the library (which probably won’t benefit from tree shaking, see “composition” on the bundlephobia page). swr would add ~4.1kb, also not benefitting from tree shaking.

wagmi is currently at 74.6kb and also requires Ethers (189kb currently) and react / react-dom (~42kb), making any dapp using wagmi ~306kb at the minimum (pre tree shaking / optimisations).This is much better than what used to be the standard not so long ago for dapps, but I think we still have a long way to go to get to a level that would be considered ideal.

As an example, I decided to use swr for use-nft, to keep its size at the absolute minimum (even though I have a preference for React Query which I use in my apps). But use-nft is a small library (5kb), and even swr represents the biggest part of its size. I am not sure this is as relevant for wagmi, since either swr or react-query would both represent a small share of the total size of the library in its current state, and other changes like moving to BigInt (rather than BN.js / BigNumber) would have a much bigger impact.

I think an important question for wagmi would be to consider React Query as an internal dependency that we could replace later (e.g. by moving to swr to optimize the library size further, after having picked the low hanging fruits), or as something that we want to expose externally, which would effectively lock it in the library.

@sammdec
Copy link
Contributor

sammdec commented Feb 10, 2022

One idea that came up in conversation earlier was what if wagmi “core” was just some basic functions that wasn’t really tied to hooks or a particular data mgmt library, this would mean it could be used it any context. Then you could build some wrapper that would essentially be a thin abstraction around react-query or swr that simply consumed the core library as async functions.
That way we wouldn’t have to worry about creating some complex adapter system and instead could more easily test the core functions and treat them as black boxes that can then simply be wrapped by a consumption layer that handles caching etc.

The one thing I havnt figured out is how you would share state as the library currently uses a context

@jxom
Copy link
Member Author

jxom commented Feb 11, 2022

Yeah, just to give a bit of background, I guess I proposed react-query as it objectively meets the criteria of what we want from a DX perspective in wagmi, albeit the larger bundle size. The main criterion being deterministic states (react-query has first class support for this). Along with other implicit criteria being: trivial programmatic retrieval and mutation of the cache (w/ partial query matching), render batching & garbage collection.

However, thinking about it, I agree w/ @shunkakinoki & @bpierre that there should be an option to still consume this library at a complete minimal surface, as there could well be minimalistic consumers. I really think @sammdec has a valid point that we could extract the core functionality out of the hooks, and consumers can consume that in whatever async state lib they want (swr, react async, react loads, etc). Which is similar to having multiple entrypoints, but a little bit more explicit. But if we were to move this to be more agnostic, and moved out of hooks into functions, I think we would need to put a bit of thought into how we deal with reactions & events (such as switching wallets, chains, etc).

Perhaps there could be:

  • an unopinionated wagmi/core entrypoint recommended for minimalists & library consumers, and
  • an opinionated wagmi/react entrypoint recommended for apps, consisting of the react-query integration which also uses wagmi/core.

Having a wagmi/core entrypoint will allow for any async state lib, along with any framework (svelte, vue, etc), to be used with wagmi.

Could be something like so for swr:

import { fetchAccount, watchAccount } from 'wagmi/core';
import useSWR from 'swr';

function Example() {
  const { data, mutate } = useSWR('account', fetchAccount);
  
  useEffect(() => {
    // If an event is emitted to change the account, then broadcast a revalidation.
    const unwatch = watchAccount({ onChange: mutate });
    return () => unwatch();
  }, []);
  
  ...
}

Or even another async state lib, like react-async:

import { fetchAccount, watchAccount } from 'wagmi/core';
import { useAsync } from 'react-async';

function Example() {
  const { data, reload } = useAsync({ promiseFn: fetchAccount });
  
  useEffect(() => {
    // If an event is emitted to change the account, then reload the data.
    const unwatch = watchAccount({ onChange: reload });
    return () => unwatch();
  }, []);
  
  ...
}

I guess it could also be nice to have a react hook to make watchAccount easier for a React consumer:

import { fetchAccount, useWatchAccount } from 'wagmi/core';
import useSWR from 'swr';

function Example() {
  const { data, mutate } = useSWR('account', fetchAccount);
  useWatchAccount({ onChange: mutate });
  
  ...
}

We could even use the core lib in another framework, like Svelte:

<script>
  import { fetchAccount, watchAccount } from 'wagmi/core';
  
  // with svelte-query
  import { useQuery } from '@sveltestack/svelte-query';
  const accountRecord = useQuery('account', fetchAccount);
  watchAccount({ onChange: $accountRecord.refetch });
  
  // with svelte swr
  import { useSWR } from 'sswr';
  const { data, mutate } = useSWR('account', fetchAccount);
  watchAccount({ onChange: mutate }); 
</script>

<div>
  ...
</div>

@tmm might have better thoughts on this,
what do you think @shunkakinoki?

@sammdec
Copy link
Contributor

sammdec commented Feb 11, 2022

What about using something like zustand to contain the state and events. https://github.com/pmndrs/zustand#using-zustand-without-react
Therefore you can still have nice abstracted state without having to get into sticky use effect hooks etc
It is also a very tiny library so would add minimal code

@bpierre
Copy link
Contributor

bpierre commented Feb 11, 2022

I really think @sammdec has a valid point that we could extract the core functionality out of the hooks, and consumers can consume that in whatever async state lib they want (swr, react async, react loads, etc). Which is similar to having multiple entrypoints, but a little bit more explicit. But if we were to move this to be more agnostic, and moved out of hooks into functions, I think we would need to put a bit of thought into how we deal with reactions & events (such as switching wallets, chains, etc).

I completely agree with the idea of extracting the core functionality out of the hooks, this is the main idea behind this proposal I made the other day: #140 (alongside replacing the error prone hybrid functions like getBalance() by a refetch() function).

On the npm modules & exports strategy

I think having a core version of the library (wagmi/core) is a great idea. Using a subdirectory might be problematic to support both ESM and CJS, since we can only use the package.json fields for the main module (wagmi). That would remove the possibility to support Node with CJS imports, which I think would be great to have for a core version of the library.

I think wagmi could be opinionated about having first class support for React, and keep providing the React + core exports of the library under the main module (wagmi). We could then use an npm scope to export the core version as a separate package, e.g. @wagmi/core, and other versoins of the library like @wagmi/vue. That way, we could use package.json files for the entry points of each version, and support both ESM + CJS among other things.

Note: if we take care of having the library fully ready for tree shaking (I can help with this), and for consumers using a bundler with tree shaking enabled, then using the core exports from wagmi should produce the same result in the final bundle than using @wagmi/core, which is a nice thing to have. The documentation could recommend using @wagmi/core to Node users (using CJS imports) and to users that don’t know if their bundler supports tree shaking.

On exposing React Query features

Otherwise I agree with @jxom, I think it makes sense to use React Query especially since the size won’t be impacted much by it (vs. swr) in the current state of things. But do we want to expose specific React Query features, or only a subset of its result object? e.g. the proposed API for storage persistence is coming straight from React Query, which means that any change in React Query would require wagmi to follow and break its own API.

Another possibility would be to expose these features but express them in a more “wagmi (i.e. simplified) way”:

import { Provider } from 'wagmi'

const App = () => (
  <Provider persist> // uses localStorage.window if `true`
    …
  </Provider>
)

Which should be easy to keep maintaining even if React Query completely changes the way persistence is expressed in their API.

Full access to React Query?

What would you guys think about (optionally) letting users gain as much access as possible to react-query? Users with advanced cases could define their own QueryClient, and the hooks could take a reactQuery parameter that would let users access set any useQuery() option, taking priority over the ones we would expose by default:

const [{ data, error, loading }] = useBalance({
  addressOrName: name || address,
  formatUnits: 'gwei',
  enabled: Boolean(name || address),
  reactQuery: {
    refetchOnReconnect: false, // advanced option requiring to pass `reactQuery` 
    enabled: false, // takes priority over `enabled` above
  }
})

And we could do the same in the returned values:

const [{ data, error, loading, reactQueryResult }] = useBalance({
  addressOrName: address,
})

// reactQueryResult would contain the full object as returned by `useQuery()`

I just wanted to put this here to discuss this possibility, but I am not sure how I feel about it personally. It would certainly give users great flexibility over the react-query behavior, but it would make parts of the wagmi API dependent on the evolution of the React Query API. It would also make the implementation less flexible, e.g. moving from useQuery to useQueries() internally to optionally support multiple results (see the example under “Less flexibility when using the hook” in my proposal) would have to be made with great care to break as little as possible the API for users. I think this might cause more issues than it would solve, and we can add it at any point in the future if it becomes needed, so it’s probably not worth adding from the start.

I feel a bit differently about letting users define their own QueryClient, because it wouldn’t really get in the way and clearly feel like an advanced part of the API:

import { Provider as Wagmi, QueryClient } from 'wagmi'

const queryClient = new QueryClient({ /* … */ })

const App = () => (
  <Wagmi queryClient={queryClient}></Wagmi>
)

…but an issue is that it makes little sense to expose QueryClient without the rest of the React Query library. And providing the entire React Query exports under wagmi, even if it doesn’t bring any conflicts, would feel weird for users. “What version does wagmi use internally?“ “Can they use the React Query exported by wagmi for their app too, or do they also need to install React Query seperately?” Which brings me to the next question 👇

React Query as a peer dependency?

By requiring users to install react-query themselves, it would have the following advantages:

  • Remove 13kb from the final bundle if the consuming app also uses React Query.
  • Make it explicit that wagmi is based on React Query. It would then split the API in two clear parts: wagmi, and React Query. For example, users would import createPersistor() or QueryClient from react-query, not wagmi (this would still be for advanced cases only, normal use cases should never have to import from react-query). If the API of React Query changes, it would not impact the wagmi API.
  • Let users upgrade React Query to the latest version (as long as it stays in the range supported by wagmi).

But it would also bring some issues:

  • pnpm add wagmi ethers is better than pnpm add wagmi react-query ethers 😉
  • Consumer apps using React Query and a bundler without deduping would still have 13kb + 13kb.
  • Same for apps using another version of React Query.
  • We’ll have to support a range of React Query versions to support, which might introduces subtle issues.

So here too, my preferred approach would be to not do this for now and keep React Query as an internal dependency, while keeping this as a possibility for later (after the refactoring).

@shunkakinoki
Copy link
Contributor

shunkakinoki commented Feb 12, 2022

@jxom Apologize for the late reply!

Completely agree with the below!

  • an unopinionated wagmi/core entrypoint recommended for minimalists & library consumers, and
  • an opinionated wagmi/react entrypoint recommended for apps, consisting of the react-query integration which also uses wagmi/core.

option to still consume this library at a complete minimal surface, as there could well be minimalistic consumers

wagmi/core seems like a very good idea, taking into consideration how wagmi could move on the extensibility of other frameworks and especially as you highlighted, to expand into other frameworks like vue and svelte

right now, my code for fetching ens integrated with swr looks like the below

import useSWR from "swr";
import { useEnsLookup } from "wagmi";

export const useEns = (address?: string, initialEns?: string) => {
  const [, lookupAddress] = useEnsLookup();

  const fetchEns = async (key, address) => {
    const result = await lookupAddress({ address });
    if (result.error) {
      throw new Error(result.error.message);
    }
    if (!result.data) {
      return null;
    }
    return result.data;
  };

  const {
    data: ens,
    error,
    mutate,
  } = useSWR(address ? ['/ens', address] : null, fetchEns, {
    fallbackData: initialEns,
  });

  return {
    isLoading: !error && !ens,
    isError: !!error,
    ens: ens,
    mutate: mutate,
  };
};

would be very nice if we could extract the lookupAddress into wagmi/core as you highlighted - a room for an unopinionated approach is always better (more so in the case of web3 as well!)

Just as a sidenote, if there is ever a need of a swr oriented opinionated approach, I am certainly down to contribute to a wagmi/swr package, of maybe develop as a fork if needed!

Again, thank you guys for your wonderful ideas

@jnsdls
Copy link

jnsdls commented Feb 13, 2022

thank you for this great proposal @jxom, we recently started integrating wagmi to handle wallet connections & signer / provider management into all of our projects @ thirdweb, we also are heavy users of react-query so this would be the perfect solution for us. (when I first saw wagmi the api interface reminded me a lot of react-query, to the point that I was actually surprised it was not built on top of it.)

needless to say we'd be excited to lend a hand helping to implement these changes, contributing fixes and improvements & long-term maintenance since we would likely have ended up forking wagmi eventually instead.

so yay for react-query 👍

couple of loose (weekend) thoughts (coming from a perspective of our own use-case)

  • as a heavy react-query user already the ideas of exposing an as-react-query-as-possible interface would be a huge selling point
  • can we make the queryClient interoperable with an existing one? I don't necessarily want to define 2 query clients (one for app-code, one for wagmi), either explicitly or implicitly (aka: I would like to just pass the one I have already into the wagmi provider)
  • regarding the peerdependency question, this is an interesting one and I think there are solid arguments for both approaches. since wagmi already has a peerdep of ethers I think it would be ok to add to that. (especially since it'll mean we can avoid the mismatching dependency problem for use-cases where the parent-app already makes use of react-query)

thanks again for the work on this so far, also was helpful to read the discussion above, we'll be watching this closely to see when and where and how we can help out!

@jxom
Copy link
Member Author

jxom commented Feb 14, 2022

Response to @bpierre:

On the npm modules & exports strategy

Yep, agree with this. I think a set of scoped packages will be nice. So essentially, we could have the following:

  • @wagmi/core*: core unopinionated & framework/library agnostic functions
  • wagmi*: React bindings for @wagmi/core w/ opinionated async fn management (react-query)
  • @wagmi/swr**: React bindings for @wagmi/core w/ opinionated async fn management (swr)
  • @wagmi/vue: Vue bindings for @wagmi/core
  • @wagmi/svelte: Svelte bindings for @wagmi/core

* = build this now
** = also build this now if @shunkakinoki is free & available

On the npm modules & exports strategy

Yeah, all this sounds good! Just to clarify, the proposed API for storage persistance is already "wagmi simplified" (I don't expose persistQueryClient to the consumer), however, I wanted the consumer to have the ability to chose between whether to use the localStorage API or something completely different (sessionStorage, cookies, etc). But maybe we could have it more simplified, and by default it persists to local storage if persist is present, otherwise, you can override persist with a persistor?

// By default, `persist` uses the local storage persistor

const App = () => (
  <Provider persist>
    <YourRoutes />
  </Provider>
)

///////////////////////////////////////////////////

// Override `persist` with a custom persistor (session storage)

import { Provider, createPersistor } from 'wagmi';

const sessionStoragePersistor = createPersistor({ storage: window.sessionStorage })

const App = () => (
  <Provider persist={sessionStoragePersistor}>
    <YourRoutes />
  </Provider>
)

Full access to React Query?

Sounds good to me, an escape-hatch to allow a user to specify any React Query argument or return value sounds good. I guess we have to keep in mind that if we expose a reactQueryResult variable in the return values, this may introduce some performance issues (useQuery triggering re-renders when unused reactQueryResult return values are changed). Something to keep in mind.

React Query as a peer dependency?

Yeah agree with all the trade-offs there. I am definitely for having React Query being an internal dependency – main reason is because it won't put such a reliance burden on this lib (if React Query upgrades to v4 - which we may not support straight away - then consumers will be waiting for us to migrate to RQ v4 before they can upgrade RQ to v4).

@shunkakinoki
Copy link
Contributor

shunkakinoki commented Feb 14, 2022

@jxom
Completely stand by the above!
I'm down to help build the wagmi/swr lib - appreciate your time for taking the time to put down your thoughts here.

@sammdec
Copy link
Contributor

sammdec commented Feb 14, 2022

@jxom Is it worth spinning up a new branch that creates the basic folder, build structure for wagmi/core, wagmi/react, hopefully that should lower the setup complexity around contributing to this new setup

@tmm
Copy link
Member

tmm commented Feb 14, 2022

Quick thoughts (more coming soon): @wagmi/core already exists! It's just called wagmi-private. We should rename (and deprecate) on npm. In addition, we can start abstracting logic from the hooks into the core package. Maybe into an actions directory that has a similar sub-directory setup as hooks (accounts, contracts, ens, etc.)?

@o-az
Copy link
Contributor

o-az commented Feb 19, 2022

I've been following this discussion for the past few days, looks very exciting. There hasn't been any update in the past 5 days, just making sure if this is still happening?

I'm available and would be more than happy to help buidl! Cheers.

@tmm
Copy link
Member

tmm commented Feb 20, 2022

The @wagmi scope on npm was taken so I renamed wagmi-private to wagmi-core in #195. I also started extracting logic from the hooks in #197 (h/t @bpierre for #140). Once all the hooks are extracted, it should make it really easy to start on adding react-query (or swr). If anyone wants to help migrate logic over to core "actions," go ahead!

This will also make it much easier for someone to add wagmi-vue, wagmi-svelte, etc.

@jxom jxom changed the title RFC: Deterministic states & caching rfc: Deterministic states & caching Feb 22, 2022
@jxom
Copy link
Member Author

jxom commented Mar 15, 2022

Going to merge this in for now. We now have an active development branch for the new @wagmi/core package that will serve as a framework agnostic wagmi client (it is undocumented – however, feel free to check out the example and even the actions/ test cases).

Right now, we are working on migrating the wagmi (react) package to integrate with React Query as per this proposal.

If anyone wants to help with a swr adaptor, or even a svelte or vue client for wagmi, feel free to reach out! With the new foundation of @wagmi/core, building these framework specific clients should be trivial.

@jxom jxom merged commit eb24256 into wevm:main Mar 15, 2022
@shunkakinoki
Copy link
Contributor

Thank you and hats off to @jxom and @tmm for leading the effort into this.
You guys are just amazing...

Hope to be able to work on wagmi/swr in april, kind of caught off this month

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

Successfully merging this pull request may close these issues.

8 participants