Skip to content

Instantly share code, notes, and snippets.

@mhuebert
Last active October 30, 2020 14:15
Show Gist options
  • Save mhuebert/d400701f7eddbc4fffa811c70178a8c1 to your computer and use it in GitHub Desktop.
Save mhuebert/d400701f7eddbc4fffa811c70178a8c1 to your computer and use it in GitHub Desktop.
React contexts with Reagent

The following provide and consume Reagent components expose React contexts without fuss.

Example

(:require [example.reagent-context :as c])

;; in a component, use `provide` to supply values for contexts:
[c/provide {:app-theme {:color "blue"}}
 ;; consume one context at a time
 [c/consume :app-theme
  (fn [theme]
    [:div
      {:style {:color (:color theme)}} "Colorful Text"])]]
  • Contexts can be keywords or React context instances. In the case of keywords, React context instances are created behind the scenes.
  • Context values are left alone, they remain as JS or Clojure values (no coercion).
  • Ratoms inside consume work as you'd expect.
  • You can provide multiple contexts at the same time, but you can only c/consume one context at a time.

Prior art: Lokeh/reagent-context

(ns example.reagent-context
(:require ["react" :as react]
[reagent.core :as r]))
(defonce get-context
(memoize
(fn [k]
(if (keyword? k)
(react/createContext (munge (str k)))
k))))
(defn provide
"Adds React contexts to the component tree.
`bindings` should be a map of {<keyword-or-Context>, <value-to-be-bound>}."
[bindings & body]
(loop [bindings (seq bindings)
out (->> body
(reduce (fn [a el] (doto a (.push (r/as-element el)))) #js [])
(.concat #js [react/Fragment #js {}])
(.apply react/createElement nil))]
(if (empty? bindings)
out
(recur (rest bindings)
(let [[context-or-key v] (first bindings)
^js context (get-context context-or-key)]
(react/createElement (.-Provider context)
#js {:value v}
out))))))
(defn consume
"Reads a React context value within component tree.
`context` should be a keyword or React Context instance."
[context f]
(react/createElement
(.-Consumer (get-context context))
#js {}
(fn [value]
(r/as-element [f value]))))
@thisgeek
Copy link

Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment