This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-02-10
Channels
- # beginners (140)
- # boot (18)
- # cider (4)
- # cljs-dev (28)
- # clojure (191)
- # clojure-greece (51)
- # clojure-russia (1)
- # clojure-spec (13)
- # clojure-uk (2)
- # clojurescript (38)
- # community-development (26)
- # core-logic (16)
- # cursive (6)
- # datomic (3)
- # defnpodcast (9)
- # editors (1)
- # emacs (1)
- # fulcro (10)
- # immutant (3)
- # jobs-discuss (2)
- # leiningen (17)
- # lumo (24)
- # off-topic (30)
- # quil (12)
- # re-frame (11)
- # reagent (103)
- # remote-jobs (2)
- # shadow-cljs (157)
- # spacemacs (4)
- # unrepl (18)
- # yada (2)
I'm using reagent and want to integrate apollo. I'm pretty close, but I have some issue 'translating' js to cljs, or rather react to reagent compents (I think). The code to be translated would be:
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
function UserList({ data } }) {
return (<div>test</div>);
}
export default graphql(gql`
query {allUsers {id name email}}
`)(UserList);
(from https://github.com/apollographql/react-apollo, simplified)I get pretty close, but the last call doesn't work so far:
(defn user-mgmt-page []
(let [graphql (wp/import "react-apollo" "graphql")
gql (wp/import "graphql-tag")
ql-query (gql "query {allUsers {id name email}}")
graphql-fn (graphql ql-query)
comp user-list]
(graphql-fn (fn [data]
[:div "test"]))))
In the last line, I tried passing r/create-class
and r/reactify
to the graphql-fn
.I know the query works fine - I can call it using an apollo client like this:
(.then (client.query #js {:query ql-query}) js/console.log)
@kurt-o-sys have you tried r/as-element
nope, let me try.
I’m only on phone so not sure on the lib completely. But you can also look into adapting r/adapt-react-component
oh, that works the other way around, doesn't it? 😛
I don’t think I understand the usage enough. If you need to take a react component from a lib to use in reagent you can wrap it with adapt react component
r/as-element
same result
yeah... ok, let me search a little more.
@kurt-o-sys are both imports on a React component? If so adapt both of them and then they can be used similar to a normal Reagent component fn
oh, so instead of trying to pass a react component to the graphql-fn
, turning them into reagent components? That would mean that graphql-fn
accepts a reagent function - doesn't sound plausible to me (since that's a js function expecting well... a js function, I thought.
What actually happens is that the UserList
function (component?) is updated whenever a component modifies data from the apollo API (whenever the query changes).
That's the aim. There seems to be a 'react' way of doing it, but I can't make it work in reagent so far.
So, the upper function UserList
is a dumb component, the lower export default
is used to well... connect the dumb component to the db state
You adapt react components to fit into your typical reagent model. Square braces etc.
If you have to call a react fn that expects a react component you do the r/as-element
So I think you have a few things going on. Some of your interop is react components (that can be a fn or a class In JS)
You may also be using just a plain JS fn. for the query. For that, you may just need to do appropriate clj js interop. Like clj->js
(defn user-mgmt-page []
(let [graphql (r/adapt-react-component (wp/import "react-apollo" "graphql"))
gql (r/adapt-react-component (wp/import "graphql-tag"))
ql-query (gql "query {allUsers {id name email}}")
]
[graphql ql-query (r/as-element (fn [data]
[:div "test"])))))
yeah, right... although actually graphql ql-query
returns a function
export default graphql(gql`
query {allUsers {id name email}}
`)(UserList);
so, first, I need to do:
graphql(gql`
query {allUsers {id name email}}
`)
and use that function and pass a react component/function
ok... makes sense.
@kurt-o-sys i’ve just gone through this interop dance (with a different library). let me know if you are still having trouble.
still having trouble 🙂 - I mean, most interop is fine, I'm only struggling with this one.
right, I guess so
this is the example they give:
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
function TodoApp({ data: { todos, refetch } }) {
return (
<div>
<button onClick={() => refetch()}>Refresh</button>
<ul>{todos && todos.map(todo => <li key={todo.id}>{todo.text}</li>)}</ul>
</div>
);
}
export default graphql(gql`
query TodoAppQuery {
todos {
id
text
}
}
`)(TodoApp);
A 'normal' react function/component being TodoApp
and using HoC to embed it in another component.
which is build by graphql(gql
...`)
Basically, there are 3 places where I have to make the interop work properly: 1. where I mount the app:
import { ApolloProvider } from 'react-apollo';
ReactDOM.render(
<ApolloProvider client={client}>
<MyRootComponent />
</ApolloProvider>,
document.getElementById('root'),
);
becomes:
;; -------------------------
;; Initialize app
(defn mount-root []
(let [LinkHTTP (-> (wp/import "apollo-link-http")
(.createHttpLink #js {:uri ""}))
InMemoryCache (wp/import "apollo-cache-inmemory" "InMemoryCache")
ApolloClient (wp/import "apollo-client" "ApolloClient")
client (ApolloClient.
(clj->js {:link LinkHTTP
:cache (InMemoryCache.)}))
ApolloProvider (wp/import "react-apollo" "ApolloProvider")]
(reagent/render [(reagent/adapt-react-class ApolloProvider) {:client client}
[main-page]]
(.getElementById js/document "app"))))
(if I got it right)
so somewhere in your code you’re going to need something like [(adapt-react-class react-component) props children]
. i’m just trying to sort out where
yeah, wait, here comes the next part 🙂
this part (replacing TodoApp with UserList):
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
function UserList({ data }) {
return (
<div>
data
</div>
);
}
export default graphql(gql`
query { ... }
`)(UserList);
becomes something like:
(defn user-list [{data :data}]
[:div data])
(defn user-mgmt-page []
(let [graphql (wp/import "react-apollo" "graphql")
gql (wp/import "graphql-tag")
query "query {allUsers {id name email}}"
enclose-with-gql (graphql (gql (clj->js [query])) (clj->js {}))
comp (-> user-list
r/reactify-component
enclose-with-gql
r/adapt-react-class)]
comp
))
I thought...
but it doesn't work properly 😛
let’s see. so i just did this with react-dnd which uses these hoc functional injectors.
this is what i ended up with:
(defn draggable-notebook-link
[notebook-id notebook-name]
(let [source-decorator (react-dnd/DragSource
"notebook"
source-spec
source-collect)
target-decorator (react-dnd/DropTarget
#js ["notebook" "doc"]
target-spec
target-collect)]
(reagent/create-element
(target-decorator
(source-decorator (reagent/reactify-component editable-notebook-link)))
#js {:notebook-id notebook-id
:notebook-name notebook-name})))
right...
let me check...
oh, right... that might make sense!
Uncaught TypeError: Cannot convert a Symbol value to a string
with the r/create-element
.
will try returning [comp]
with`r/adapt-react-class` I get: https://reactjs.org/docs/error-decoder.html?invariant=31&args[]=object%20with%20keys%20%7Bvariables%2C%20refetch%2C%20fetchMore%2C%20updateQuery%2C%20startPolling%2C%20stopPolling%2C%20subscribeToMore%2C%20loading%2C%20networkStatus%2C%20error%7D&args[]=
lol, be back in a minute 😛
question: in the javascript code, graphql took 1 argument but here you are passing it 2. is that right?
oh, euh, no?
oh yeah, I do.
doesn't matter, it's an optional map with parameters.
I'll remove the second one.
enclose-with-gql (graphql (gql (clj->js [query])))
hm. i tried changing my code to
[(reagent/adapt-react-class
(target-decorator
(source-decorator (reagent/reactify-component editable-notebook-link))))]
{:notebook-id notebook-id
:notebook-name notebook-name}))
to see if i understand what’s going on. but that doesn’t work.when I log things:
comp -> {"id":null,"class":null}
comp is pretty empty.ooh, it works! 😛
(defn user-list [{data :data}]
[:div data])
was the problem (the last problem) I had 😛data is an object, not something that can be printed.
(defn user-list [{data :data}]
(js/console.log (str "data -> " data))
[:div (js/JSON.stringify data)])
shows me the data in my browser 😛
without errors!
nice, thx, cool!!
great! just for the record, this:
[(reagent/adapt-react-class
(target-decorator
(source-decorator (reagent/reactify-component editable-notebook-link))))
{:notebook-id notebook-id
:notebook-name notebook-name}]))
and this (reagent/create-element
(target-decorator
(source-decorator (reagent/reactify-component editable-notebook-link)))
#js {:notebook-id notebook-id
:notebook-name notebook-name})))
ARE equivalent, (although there is some camel/kebab shenanigans in the argument passing that confused me)oh, nice, thx!
so the create-element should work as well? Will try. Great.
someone should write a blog how to do this...
i often think i should write a blog about something but i don’t have a blog so i don’t do it 🙂
the one thing i’m not sure was a good idea is that reagent functions used to take a props and children argument instead of taking an arbitrary list. interop would be considerably less confusing if they did it the old way
same 🙂
I don't have a blog either.
should start one 🙂
i think the angle i’d like to take is “reagent for experienced javascript/react programmers”
maybe, or not. I'm not an experienced js or react programmer.
I just happen to like cljs and the idea behind react, and reagent is the normal way to go. So I need to translate all that js/react stuff all the time.
It's something that turns many people down, that interop.
that’s a good point. i won’t call it that then because understanding the inner workings of reagent is just plain useful for doing interop
oh, that's just a wrapper to import webpack stuff.
(defn import
([module]
(aget js/window.deps module))
([module object]
(aget js/window.deps module object)))
(`import` may not be the best name, though)
it's just what js import
does.
it’s a small but useful wrapper around the gcl getters, which is supposedly what we’re supposed to be using instead of aget
oh, ok, thx!
i’m also moving over to shadow-cljs right now because i do a lot of interop. it’s pretty slick if you have an afternoon to mess around with it
yeah... heard about it. For this project, I can't change anymore. I will for other ones. Need to check it first.