homebaseio / homebase-react

The React state management library for write-heavy applications
https://homebase.io/docs/homebase-react
MIT License
202 stars 10 forks source link

Request for documentation of usage without react #80

Closed yqrashawn closed 3 years ago

becomingbabyman commented 3 years ago

Hi @yqrashawn we've been considering exposing an alternative API to React Hooks. At the moment we don't export any alternative APIs.

Are you looking to integrate with another view framework like Vue? Or are you just looking for direct datascript DB access in JS?

If you want datascript without any of the extra view level caching homebase-react does then you could try using the datascript JS API directly. The syntax is not as JS friendly as homebase-react, but it supports more features because it does not try to mask over a lot of Datalog and Clojure data.

Regarding view level caching, we are considering rewriting our cache to work with other frameworks. It's still in the early stage of design, but a rewrite will also help us integrate with datahike's async JS API.

yqrashawn commented 3 years ago

I'm looking for some kind of a subset of datascript api that is js-friendly. I'm writing web extensions which

  1. don't care too much about the bundle size
  2. don't have a backend
  3. have lots of data

Datascript is really suitable for this. It's just I need to come up with a js friendly api for my co-workers.

I'll close this issue since I'm already working on a custom solution. Thanks for you quick answer.

becomingbabyman commented 3 years ago

@yqrashawn if what we’ve got right now looks good for your use case we could export the JS API in addition to the react hooks we already export. Let me know if that’s of interest to you. It’s not much work on our end and we’ve been considering doing it for a while.

MioQuispe commented 3 years ago

@yqrashawn if what we’ve got right now looks good for your use case we could export the JS API in addition to the react hooks we already export. Let me know if that’s of interest to you. It’s not much work on our end and we’ve been considering doing it for a while.

Hey, Im using this with react. Love it, great work. but even with react the hooks dont seem to be enough unfortunately.

in my case I have this problem:

const onDragEnd = (event) => {
  // need to query database for entity with event.active.id
}

Hooks dont allow for this :cry:

becomingbabyman commented 3 years ago

Thanks @MioQuispe.

I hear you on the need for client.query(…) and client.entity(…) functions so you can read from the DB without using a react hook.

The main issue at the moment is not wanting to introduce potentially unstable APIs that will break in the near term. Namely we’re working on allowing people to use datahike as an alternative to the current datascript database.

Datahike supports persistence in IndexedDB, but to do that it needs to be async. We’ve planned the react hooks APIs to allow for future async support, but the internal query and entity functions are still sync since we don’t support Datahike yet and are about to refactor the internal cache to support async.

That said I think we could wrap the query and entity functions in promises and then put them on the client to simulate the future async APIs… I think that would be a stable API long term and provide query support outside of hooks. What do you think?

MioQuispe commented 3 years ago

Thanks @MioQuispe.

I hear you on the need for client.query(…) and client.entity(…) functions so you can read from the DB without using a react hook.

The main issue at the moment is not wanting to introduce potentially unstable APIs that will break in the near term. Namely we’re working on allowing people to use datahike as an alternative to the current datascript database.

Datahike supports persistence in IndexedDB, but to do that it needs to be async. We’ve planned the react hooks APIs to allow for future async support, but the internal query and entity functions are still sync since we don’t support Datahike yet and are about to refactor the internal cache to support async.

That said I think we could wrap the query and entity functions in promises and then put them on the client to simulate the future async APIs… I think that would be a stable API long term and provide query support outside of hooks. What do you think?

Yeah that sounds reasonable to me.

Btw, a few other hiccups I've had. Might be good to know:

-Ids have to be numbers, but only in some cases? It was a bit confusing. Im hoping to use this with dfinity & ipfs and was hoping to have the id's be either principals or cids. So strings basically. If its not possible thats ok. But thought I'd mention it. Also it doesn't seem to accept 0 as an id, which took a while to figure out. Its not the end of the world, but might be good to mention in the docs if it cant be changed.

-Would be great with entity.get("*") / entity.getAll() or something similar, so you don't have to do it manually for each prop

Hope thats helpful! Otherwise very intuitive and great experience.

becomingbabyman commented 3 years ago

Glad to hear it. I prototyped the idea last night an it looks like it should be fine. I’ll link the PR when it’s up.

Re IDs we’re working with the Datahike team on idiomatically supporting distributed forms of identity assignment, like UUIDs. For now I do not recommend modifying the default thing.id and instead adding your own thing.cid or thing.principal attribute. If you want to lookup entities by that attribute just set it to unique in the schema and then you can do useEntity({ thing: { cid: “abc123” } }). In general I don’t recommend treating the generated ids as stable since this is currently an in-memory system and they are liable to change if the data in the database is rehydrated in a different order. This will change when we introduce persistence to IndexedDB and UUID support, but for now you’ve got to manage these bits on your own.

Re entity.get(“*”) can you talk a bit more about your use case? I ask because there are a lot of ways to go about implementing this. The underlying datalog DB does support patterns similar to this, but they have different tradeoffs when connecting them to React and doing the live updating. At the moment we treat entity.get(“specificAttribute”) as a whitelist, so we only cache data and rerender components when those specific attributes change. If debugging is the issue we just released custom chrome formatters so you can log out entities and inspect all their attributes in the console. We’re also working on a chrome extension to read and eventually write to the database from a GUI. If it’s something more like GraphQL that you want, we’re talking about exposing the pull syntax which is essentially GraphQL. I’m hesitant to just expose an entity.get(“*”) function since that would have severe performance costs in the context of React if used naively, and it’s ripe for naive use since it’s probably the first thing a new developer would try.

Thanks for all the feedback @MioQuispe

MioQuispe commented 3 years ago

Glad to hear it. I prototyped the idea last night an it looks like it should be fine. I’ll link the PR when it’s up.

Re IDs we’re working with the Datahike team on idiomatically supporting distributed forms of identity assignment, like UUIDs. For now I do not recommend modifying the default thing.id and instead adding your own thing.cid or thing.principal attribute. If you want to lookup entities by that attribute just set it to unique in the schema and then you can do useEntity({ thing: { cid: “abc123” } }). In general I don’t recommend treating the generated ids as stable since this is currently an in-memory system and they are liable to change if the data in the database is rehydrated in a different order. This will change when we introduce persistence to IndexedDB and UUID support, but for now you’ve got to manage these bits on your own.

Re entity.get(“*”) can you talk a bit more about your use case? I ask because there are a lot of ways to go about implementing this. The underlying datalog DB does support patterns similar to this, but they have different tradeoffs when connecting them to React and doing the live updating. At the moment we treat entity.get(“specificAttribute”) as a whitelist, so we only cache data and rerender components when those specific attributes change. If debugging is the issue we just released custom chrome formatters so you can log out entities and inspect all their attributes in the console. We’re also working on a chrome extension to read and eventually write to the database from a GUI. If it’s something more like GraphQL that you want, we’re talking about exposing the pull syntax which is essentially GraphQL. I’m hesitant to just expose an entity.get(“*”) function since that would have severe performance costs in the context of React if used naively, and it’s ripe for naive use since it’s probably the first thing a new developer would try.

Thanks for all the feedback @MioQuispe

Amazing! Gonna test it immediately.

re: IDs, gotcha. That should work.

re: entity.get("*"), this is roughly what I'm trying to do.

const onDragEnd = (event) => {
  if (event.condition) {
    transact([{
      entity: {
        name: baseEntity.get("name"),
        component: baseEntity.get("component"),
        width: baseEntity.get("width"),
        height: baseEntity.get("height"),
        ownerId: baseEntity.get("ownerId"),
        // +maybe some other props I don't know about? and those could vary depending on the entity too.
      }
    }])
  }
}

So again this is just querying the database directly, no need to subscribe to anything. but on the other hand if it is only exposed when querying the database directly, that could feel a bit inconsistent?

I'm actually not sure yet I will need it to be this dynamic, so hardcoding the props is okay in the meantime. but it was something I thought would be there and went looking for in the docs.

Cheers.

MioQuispe commented 3 years ago

Oh and another thing.

Is it mandatory for the hooks to be wrapped in arrays?

const [entity] = useEntity(0);
const [queryResult] = useQuery({})
const [transact] = useTransact()
const [client] = useClient()

Seems a little unnecessary?

becomingbabyman commented 3 years ago

Oh and another thing.

Is it mandatory for the hooks to be wrapped in arrays?

const [entity] = useEntity(0);
const [queryResult] = useQuery({})
const [transact] = useTransact()
const [client] = useClient()

Seems a little unnecessary?

Yes, it is a bit silly looking at the moment.

Eventually, the API will support optional 2nd and 3rd return values. E.g.

const [entity, errors, loading] = useEntity(0)

The reason to use the arrays now is to cut down on breaking changes in the future.