11

I have a reasonably large React + Relay codebase that is being built using Webpack. Is it possible to gradually introduce ClojureScript + Reagent into this somehow?

I was thinking of starting with some of the smaller functional components in our code base and swapping them out. This would mean that the Reagent component would somehow need to receive props from the parent.

Any thoughts or tooling around doing this? A quick Google only seems to turn up articles around including JavaScript libraries in your ClojureScript app, not the other way around.

3
  • Have you seen this article? blob.tomerweller.com/reagent-import-react-components-from-npm Commented Jan 30, 2017 at 16:09
  • Like I said in the question, I found a lot of articles about importing JS into your existing ClojureScript project, but nothing about using ClojureScript with an existing JS project. Commented Jan 31, 2017 at 3:47
  • You'd have to find a clojurescript-loader for webpack then. Commented Feb 1, 2017 at 9:09

1 Answer 1

8

You could create a reagent component, export it, and then import it in your JS code.

Since you already have webpack/react in your js project, you need to exclude those from your cljs project:

:dependencies [[org.clojure/clojure "1.8.0"]
               [org.clojure/clojurescript "1.9.229"]
               [org.clojure/core.async "0.2.391"
                :exclusions [org.clojure/tools.reader]]
               [reagent "0.6.0" :exclusions [cljsjs/react cljsjs/react-dom cljsjs/react-dom-server]]]

Now you have to trick Reagent into thinking that those react files you've just excluded are still here. Create these three files:

src/cljsjs/react.cljs

(ns cljsjs.react)

src/cljsjs/react/dom.cljs

(ns cljsjs.react.dom)

src/cljsjs/react/dom/server.cljs

(ns cljsjs.react.dom.server)

Yes, just one line with namespace declaration in each file.

Now you can write your component:

reagent_component/core.cljs:

(ns reagent-component.core
    (:require [reagent.core :as r]))

(def counter (r/atom 5))

(def ^:export test66
    (r/create-class
        {:reagent-render
         (fn [props]
             (let [{:keys [name]} props]
                 [:div 
                  {:on-click (fn [] (swap! counter inc))}
                  name ", I am counting " (clojure.string/join ", " (map inc (range @counter)))])
             )}))

Your cljsbuild section in project.clj might look like this:

:cljsbuild {:builds
            [{:id           "min"
              :source-paths ["src"]
              :compiler     {:output-to     "resources/public/js/compiled/reagent_component_min.js"
                             :output-dir    "resources/public/js/compiled/min"
                             :main          reagent-component.core
                             :optimizations :advanced
                             :asset-path    "js/compiled/out"
                             }}]}

For the purpouse of brevity I've given you only the min section of the cljsbuild.

lein cljsbuild once min will produce resources/public/js/compiled/reagent_component_min.js file, which must be copied into your Webpack project.

New entry is added to your webpack.config before your main entry point:

 entry: [`${APP_DIR}/reagent_component_min.js`, `${APP_DIR}/main.js`],

This file (reagent_component_min.js) should be excluded from babelification:

module: {
  loaders: [
    {
      test: /\.js$/,
      exclude: /(node_modules|bower_components|reagent_component_min.js)/,
      loader: 'babel-loader',
      query: {
        presets: ['latest']
      }
    }
  ]
}

and in your javascript you use it like this:

import React from 'react';
import ReactDOM from 'react-dom';

const comp = reagent_component.core.test66;

const element = <div>Hello, world
    {React.createElement(comp, {name: 'akond'})}
</div>;

ReactDOM.render(
  element,
  document.getElementById('app')
);

Yes, babel jsx plugin does not recognize <comp name="..."/>. That's why React.createElement is called. I didn't figure out how to make it look better, but it works.

Sign up to request clarification or add additional context in comments.

1 Comment

For a JSX to interpret a variable as a tag, use a capital letter: const Comp = ...; <Comp name="akkond" />

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.