[Updated]: How to replace React components using PureScript's React libraries

Ah, I still don’t really understand that…

  1. It’s a linter rule, not a React rule (and technically the function is named correctly, just not in the style the linter wants)
  2. Linters should never run on generated code. No code generator could possibly make all linters happy with the same emitted code.

Any interest in adding a react-basic-hooks section? I’ve found interop to be even easier since you use the same paradigm for both PS and JS components, and if you end up needing to write a stateful component in an FFI file the hooks api lets you skip JS classes altogether (classes are extra tedious without the ES2016 class keyword).

3 Likes

I am interested in that, though I’ll have to learn about it first :slight_smile: I’d love to see how that works. Also I’m happy to link out to related articles if you’ve written anything on this topic!

1 Like

I tried to port your tutorial to the hooks API here (and attempted a custom hook in Form.purs): https://github.com/ptrfrncsmrph/purescript-react-tutorial
Would be interested in feedback as I’m new to PureScript and there’s maybe some language features I’m not taking advantage of.

2 Likes

How to get over the ‘module’ is not defined and ‘require’ is not defined errors while trying to use the generated js file?

Do you have any code or steps taken you can share? I didn’t encounter this error myself walking through the steps in the guide from scratch.

I’m a little surprised to see require is not defined, as that sounds like an issue with JavaScript ES6 modules. The article uses the import syntax import React from "react"; which works fine. I found this issue on the create-react-app repository which may match the issue you’re seeing.

However, if you are just going through the article as-is, then I don’t know why you’re encountering that error.

So if I generate a component in the output folder and use that as a replacement in the component where the JS version was used(which uses ES6) I get errors involving module and require

PureScript generates CommonJS modules, which are imported with require by default. However, build tools like Webpack and Parcel make it possible to use the more common import statement instead. The article uses Webpack so this is possible, but if you aren’t using one of these tools then I believe you have to import with require.

See:

as well as these issues on the PureScript compiler:

I’ve only used projects with tools like Webpack and Parcel, though, so I don’t have firsthand experience of how to do things without them. If you are using one of these, then I’m not quite sure what the issue is.

I ran into this issue too when launching a react project that bypassed create-react-app (so no webpack setup automatically). To follow-up on the previous post, here’s a parcel command that resolves this require error:

parcel serve assets/index.html -o index--parcelified.html --open

This assumes assets/index.html points to the js script generated from your purescript code.

@thomashoneyman Is your article hosted somewhere that takes PRs? I noticed some inconsistencies when I went through the tutorial. Also, is the final result tracked? Linking to something like @ptrfrncsmrph’s react-hooks port would be a helpful fallback reference.

@milesfrain My writing is on GitHub but in a private repository but I really ought to move the writing out to its own repository which can accept pull requests. I simply haven’t gotten around to it. And that’s a good idea to link to the react-hooks port, doing that now (Edit: updated on the site).

In the meantime, feel free to post article edits here or to send them to me (my email is on my site at https://thomashoneyman.com/open-invitation).

Thanks for the link! This was the first thing I attempted in PureScript, so for my part I’ve tried to polish it up with a few things I’ve learned since, but I’ve really only dabbled in the language. If there is anything in there that should be corrected, please let me know!

Seems good to me, though I usually inline render functions like renderCounter. Typing out the hooks type can be tedious and usually isn’t necessary.

1 Like

I edited the initial post in this thread to reflect that this article has been updated to use react-basic-hooks. It also describes some new conveniences with create-react-app from @andys8 for bootstrapping hybrid PureScript / JavaScript projects. If you haven’t read the original or you’re interested in a refresher the post is updated now!

4 Likes

What is this Data.Interpolate magic?! :exploding_head:

Should these names be reversed?

+mkCounter :: Props -> JSX
+mkCounter = unsafePerformEffect counter
+
 counter :: Component Props
+import { mkCounter as Counter } from "./MyApp/Components/Counter.purs";
1 Like

It feels more natural to me to import counter when using this in PureScript code and for something like mkCounter to exist when exporting this via the FFI. But I’m open to changing my mind if I’m missing something here! I’ve only toyed with react-basic-hooks as we use the low-level bindings to react at work.

1 Like

Nate came up with this idea here: Feature request: String interpolation

It’s a neat trick where instance chains recursively act like Semigroup appends over the first argument.

class TCName a where
  functionName :: SemigroupLikeType -> a

instance first :: TCName SemigroupLikeType where
  functionName :: SemigroupLikeType -> SemigroupLikeType
  functionName value = value
else instance second :: TCName a => TCName (SemigroupLikeType -> a) where
  functionName :: SemigroupLikeType -> (SemigroupLikeType -> a)
  functionName firstSemi = 
    \secondSemi -> functionName (firstSemi <> secondSemi)
else instance third :: (OtherConstraintsNeeded b, TCName a) => TCName (b -> a) where
  functionName :: SemigroupLikeType -> (b -> a)
  functionName firstSemi = 
    \b -> functionName (funcFromOtherConstraints firstSemi b)
1 Like

Ah, ok. It’s very much up to you :slight_smile: I just started using that convention when the exports started becoming effects instead of the components themselves. That way they’d get imported as mkX instead of x, then used as x <- mkX as the parent component is constructed, and its eventual use in the render function would remain x. We also sometimes continue to export components using unsafePerformEffect do component ... and in those cases continue using the x name as the export.

I missed that transition (when making components became effectful) – does that mean that you now need to do this when using them? (In that case my article is actually a bit incorrect and I should make an update :slight_smile: )

component = ...
  child1 <- mkChild1
  child2 <- mkChild2
  
  pure $ div_ [ child1, child2 ]

It’s usually like this: Root.purs (though this uses a custom monad instead of Effect, same thing though)

-hooks has always used Effect, but -classic and -compat never have, which can result in unexpected bugs when those older component creation functions aren’t used at the module level (unexpected remounts/rerenders, dropped component state, etc). Halogen uses slots and static typing to avoid this I believe, but internally React uses function instance reference equality (plus a key prop if it exists`) to determine component identity. Since JS isn’t statically typed there’s no safe way to reuse the old component state or props if the component function changes.

1 Like