Spotted Arrow

2017-08-28

Write React Components in PureScript with Pux

I am still on the PureScript bend after writing about how you can port over your Elm code into PureScript with Pux. I really enjoyed writing about that topic and it got me thinking about how I can start using PureScript right now. This is a bit optimistic, though. Even with the knowledge I now have of creating React components using Pux, I would still say it’s a bit of a stretch to think I could pull in PureScript into a project at work, but a man can dream can’t he?! 😂

To get started create a new React application. You will need webpack, babel, ES2015+, babel-core, babel-loader, and the kitchen sink. No, I will not torture you with creating you own webpack.config.js. Instead, to quickly get up and running, use create-react-app.

npm i -g create-react-app # Install it globally
create-react-app ps-and-react # cd into some dir and create a dir
cd ps-and-react && yarn install && yarn start

Once you run the commands above you should see a fully functional application running in your browser at localhost:3000.

Inside of the src directory create a PureScript project scaffold with pulp.

mkdir PureScript
cd PureScript
pulp init

Once the project is scaffolded out you will just need to install Pux and React.

bower install --save purescript-pux purescript-react

Create a file called Counter.purs and copy pasta this code which basically mimics Pux’s example counter tutorial.

module Counter where

import Prelude hiding (div)

import Control.Monad.Eff (Eff)
import Pux (CoreEffects, EffModel, start)
import React (ReactClass)
import Pux.DOM.Events (onClick)
import Pux.DOM.HTML (HTML)
import Pux.Renderer.React (renderToReact)
import Text.Smolder.HTML (button, div, span)
import Text.Smolder.Markup (text, (#!))

type State = {
  count :: Int
}

data Event = Increment | Decrement

foldp ::  fx. Event -> State -> EffModel State Event fx
foldp Increment state = { state: state { count = state.count + 1 }, effects: [] }
foldp Decrement state = { state: state { count = state.count - 1 }, effects: [] }

view :: State -> HTML Event
view state =
  div do
    button #! onClick (const Increment) $ text "Increment"
    span $ text (show state.count)
    button #! onClick (const Decrement) $ text "Decrement"

toReact ::  props fx. State -> Eff (CoreEffects fx) (ReactClass props)
toReact state = do
  app <- start
    { initialState: state
    , view
    , foldp
    , inputs: []
    }

renderToReact app.markup app.input

You will notice there is one thing that is different here. Here you have a function toReact that returns a ReactClass given props. Usually this type of setup is reserved for the main function. But in this case you want to export this setup in the function itself.

Make sure to compile before moving to the next step.

pulp build

Now head on over to App.js at the root of the React src folder and pull in the Counter from PureScript.

import React, { Component } from "react";

import PSCounter from "./PureScript/output/Counter";

class App extends Component {
  render() {
    const Counter = PSCounter.toReact({ count: this.props.count })();
    return (
      <div className="App">
        <Counter />
      </div>
    );
  }
}

export default App;

Next, pass in the count prop to App in index.js.

ReactDOM.render(<App count={1} />, document.getElementById("root"));

When you run yarn start again you should see a fully functional React component made with PureScript and Pux!

If you haven’t done so already, I recommend looking at the book PureScript by Example as well as the PureScript Documentation to get some more background. Also, check out all the library document on Pursuit. If you have any questions check out the Slack or Gitter channels. Until next time, keep hacking!

You can find the above code in this repo below:

This article has Webmentions