nosco / hx

A simple, easy to use library for React development in ClojureScript.
MIT License
247 stars 16 forks source link

React `createContext` wrapper #19

Closed garrett-hopper closed 5 years ago

garrett-hopper commented 5 years ago

Are there any plans to add wrappers for things such as createContext (similar to the dummy wrappers in hx.hooks such as <-reducer)?

It would be nice to do some Clojure wrapping to for easily getting out the Consumer and Provider by key.

lilactown commented 5 years ago

I don't see why not - PRs welcome! I might get around to this during the weekend.

In the meantime I'm going to try and clean up the code so that it's easier for others to jump into 😅

dawranliou commented 5 years ago

Hey I'd like to help with this. Any feedback would be helpful because I'm not very familiar with createContext.

I'm thinking about adding the wrapper function that returns a map containing the producer and consumer:

(defn context
  "Use react/createContext and return a map of Producer and Consumer."
  [value]
  (let [context (react/createContext value)]
    {:provider (.-Provider context)
     :consumer (.-Consumer context)}))

However, I'm not sure how to test this function. Would you give me some suggestion? Thank you!

dawranliou commented 5 years ago

I just found the function hx.hooks/<-context. It'd be better if the output of the above context function can be passed into <-context too. I'm thinking of making <-context to a multimethod to handle both the map scenario and the React.Context object scenario. What's your thought on this solution?

lilactown commented 5 years ago

Hmm good point about <-context. Perhaps a differect tactic is in order.

One thing to note is that most libraries expose Provider and Consumer components separately, rather than exposing the raw Context value returned by createContext.

So the case we're talking about is most useful when we're using a Context value we've created ourselves.

Another option: we could add a new hiccup element, :provider, that would allow you to pass in a Context value and it would create the Provider for you.

Example:

(def my-context (react/createContext))

(defnc Child [_]
  (let [value (<-consume my-context)]
    [:div value]))

(defnc App [_]
  [:provide {:context my-context
             :value "something"}
   [Child]])

I can think of other ways, like trying to get crafty with protocols or whatnot, but probably not worth the effort.

dawranliou commented 5 years ago

Thanks for responding @Lokeh !

One thing to note is that most libraries expose Provider and Consumer components separately, rather than exposing the raw Context value returned by createContext. So the case we're talking about is most useful when we're using a Context value we've created ourselves.

I'm not sure if I understand you correctly. Do you have an example that exposes Provider and Consumer separately? I'm just not sure which would be more valuable/desirable: a) expose raw Context, or b) expose Provider and Consumer, or c) all of them. (Sorry I don't have much experience with the context object.)

I like your example above. It feels a lot like what I'd rather write in clojure instead of thinking the raw js objects. Do you think this is a separate PR?

lilactown commented 5 years ago

Sure, an example would be the way the Emotion CSS library does theming: https://github.com/emotion-js/emotion/blob/master/docs/theming.md

They expose a ThemeProvider component that you pass in the current theme to. You then can access theme theme prop in any styled components or using a withTheme higher-order component.

In this case we don't need to do anything fancy, we can just use the ThemeProvider and withTheme HOC as usual.

The annoying bit is when, in CLJS, we want to create and use context. Right now, you have to use JS interop to get at the Provider component.

This isn't super heinous to me but I see why people might not like it. So I think that to aid with this, we should have a :provider component that you pass in the context value created by react/createContext and it will pull the provider out for you.

We could also have a hx.react/create-context function that just proxies react/createContext since requiring React just for creating context is a bit annoying.

At the end of the day this is all small QoL enhancements so not super high on my list. But PRs welcome!

dawranliou commented 5 years ago

Thanks, I see how this is useful now. I would love to work on the PR for this. Thanks for taking the time explaining.

lilactown commented 5 years ago

Closing this with #25 merged