Code Monkey home page Code Monkey logo

purescript-thermite's Introduction

purescript-thermite

Pursuit

purescript-thermite is a PureScript wrapper for purescript-react. It does not provide all of the functionality of React, but instead provides a clean API to the most commonly-used parts of its API. It is possible to use purescript-react for more specialized use cases.

Building

bower update
pulp build
pulp test -r cat > html/index.js

You can also now use npm test to run the test command above.

Getting Started

Thermite components are defined in parts:

  • A type of actions, which represents the actions a user can take on our component
  • A type of states, which represents the internal state of our component
  • An initial state
  • A rendering function, which takes the current component state and properties, and creates a HTML document
  • A function which interprets actions, by modifying the state and/or running some (possibly asynchronous) computations

Here is an example. We'll build a component which displays the value of a integer-valued counter.

First of all, we need to import some modules:

import Thermite as T

import React as R
import React.DOM as R
import React.DOM.Props as RP
import ReactDOM as RDOM

In our component, users will be able to take two actions - increment and decrement - which will be represented as buttons later:

data Action = Increment | Decrement

The state of our component is just an integer:

type State = { counter :: Int }

The initial state is zero:

initialState :: State
initialState = { counter: 0 }

Our rendering function uses the React.DOM.* modules to create a HTML document containing a label and two buttons. The buttons' onclick handlers are given functions which generate the correct actions. The dispatch function, which is passed as the first argument to render, can be used to build such a function, by providing an action:

render :: T.Render State _ Action
render dispatch _ state _ =
  [ R.p' [ R.text "Value: "
         , R.text $ show state.counter
         ]
  , R.p' [ R.button [ RP.onClick \_ -> dispatch Increment ]
                    [ R.text "Increment" ]
         , R.button [ RP.onClick \_ -> dispatch Decrement ]
                    [ R.text "Decrement" ]
         ]
  ]

The performAction function interprets actions by passing a function to the state update function, which is responsible for updating the state using record updates:

performAction :: T.PerformAction State _ Action
performAction Increment _ _ = void (T.cotransform (\state -> state { counter = state.counter + 1 }))
performAction Decrement _ _ = void (T.cotransform (\state -> state { counter = state.counter - 1 }))

Note: PerformAction returns a coroutine, which can emit many asynchronous state updates using cotransform. This approach also allows us to create asynchronous and/or chunked action handlers (using AJAX or websockets, for example):

getIncrementValueFromServer :: Aff Int

performAction :: T.PerformAction State _ Action
performAction Increment _ _ = do
  Just amount <- lift getIncrementValueFromServer
  void $ T.cotransform $ \state -> state { counter = state.counter + amount }

With these pieces, we can create a Spec for our component:

spec :: T.Spec State (T.WithChildren ()) Action
spec = T.Spec {performAction, render}

Note that the new purescript-react needs some typechecking assistance for props - Spec needs an extra { children :: Children | props } field in its props, yet that field is not necessary when creating a react element with its props argument.

WithChildren props is just an alias for { children :: Children | props }.

Finally, in main, the defaultMain function from the purescript-thermite-dom library can be used to render our component to the document body by specifying the initial state:

import Thermite.DOM (defaultMain)

main = defaultMain spec (const initialState) "MyComponent" {}

Combining Components

The Spec type is an instance of the Semigroup and Monoid type classes. These instances can be used to combine different components with the same state and action types.

In practice, the state and action types will not always match for the different subcomponents, so Thermite provides combinators for changing these type arguments: focus and foreach. These combinators are heavily inspired by the OpticUI library.

See the example project for examples of these kinds of composition.

focus

focus (and the related functions focusState and match) are used to enlarge the state and action types, to make it possible to embed a component inside a larger component.

focus takes a lens, which identifies the state type as a part of the state type of the larger component, and a prism, which identifies all actions of the smaller component as actions for the larger component. focusState is used when only the state type needs to be changed, and match is used when only the action type needs to be changed.

As a simple example, we can combine two subcomponents by using a Tuple to store both states, and Either to combine both sets of actions:

spec1 :: Spec S1 _ A1
spec2 :: Spec S2 _ A2

spec :: Spec (Tuple S1 S2) _ (Either A1 A2)
spec = focus _1 _Left spec1 <> focus _2 _Right spec2

Here, _1 and _Left embed spec1 inside spec, using the left components of both the state Tuple and the Either type of actions. _2 and _Right similarly embed spec2, using the right components.

focus is responsible for directing the various actions to the correct components, and updating the correct parts of the state.

split

split is used to handle child components which might not be present, for example, when a parent object contains a Maybe state.

type Parent = { child :: Maybe child }

_child :: LensP Parent (Maybe Child)
_child = lens _.child (_ { child = _ })

_ChildAction :: PrismP ParentAction ChildAction

childSpec :: Spec Child _ ChildAction

spec :: Spec Parent _ ParentAction
spec = focus _child _ChildAction $ split _Just childSpec

foreach

Where focus embeds a single subcomponent inside another component, foreach embeds a whole collection of subcomponents.

foreach turns a Spec eff state props action into a Spec eff (List state) props (Tuple Int action). Note that the state type has been wrapped using List, since the component now tracks state for each element of the collection. Also, the action type has been replaced with Tuple Int action. This means that when an action occurs, it is accompanied by the index of the element in the collection which it originated from.

purescript-thermite's People

Contributors

akst avatar athanclark avatar chexxor avatar dependabot[bot] avatar doolse avatar dpatti avatar emk avatar eriknstevenson avatar ethul avatar farnoy avatar fisx avatar fluffynukeit avatar jdreutt avatar jkbits1 avatar justinwoo avatar kofno avatar np avatar nwolverson avatar ondrejslamecka avatar paf31 avatar passy avatar rintcius avatar spencerjanssen avatar tellnobody1 avatar toddbernhard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purescript-thermite's Issues

Handling a single optional child with Maybe

I've spent some time coding with Elm, Halogen and Thermite, and I'm really delighted with Thermite's simplicity and flexibility. It hits a sweet spot for me.

Right now, Thermite handles single child elements which are always present (using focus), and lists of 0 to N child elements (using foreach). But I couldn't find any way to handle a single child element which may or may not be present.

What I would love to have is a function with the signature:

maybeSpec :: forall eff props state action.
  T.Spec eff state props action ->
  T.Spec eff (Maybe state) props action

I believe this could be implemented inside Thermite as:

maybeSpec (T.Spec spec) = T.Spec
  { performAction: performAction
  , render: render
  }
  where

  performAction :: T.PerformAction eff (Maybe state) props action
  performAction a p Nothing k = pure unit
  performAction a p (Just st) k = spec.performAction a p st k

  render :: T.Render state2 props action2
  render k p Nothing = []
  render k p (Just st) = spec.render k p st

But the T.Spec constructor is private, and so if we need to implement this outside of Thermite, it gets slightly tricky:

maybeSpec spec = T.withState makeSpec
  where
    makeSpec Nothing = T.simpleSpec T.defaultPerformAction T.defaultRender
    makeSpec (Just state) = T.focus justLens id spec

    justLens :: LensP (Maybe state) state
    justLens = lens Unsafe.fromJust (\_ v -> Just v)

Here is some sample code showing how this might be used:

type State = { player :: Maybe VideoPlayer.State }
data Action = PlayerAction VideoPlayer.Action

_PlayerAction :: PrismP Action VideoPlayer.Action
_PlayerAction = prism PlayerAction \pa ->
  case pa of
    PlayerAction act -> Right act

_player :: LensP State (Maybe VideoPlayer.State)
_player = lens _.player (_ { player = _ })

app :: forall props. T.Spec AppEffects State props Action
app
  = T.simpleSpec performAction render
  <> T.focus _player _PlayerAction (maybeSpec VideoPlayer.videoPlayer)

If you think that it would be worth supporting something like maybeSpec, I'm happy to submit a PR (and to rename it to a better name if you can think of one).

And thank you very much for Thermite! I'm finding that it's the most productive of all the React-style strongly-typed libraries that I've tried.

Server Side Rendering in Thermite

How can we reliable render the pages after loading the data from database and then render that to string and return to the browser?

https://github.com/purescript-contrib/purescript-react-dom/blob/master/src/ReactDOM.purs#L32

I can use the renderToString from the ReactDOM package.

The issue here is... using CoTransform and Aff and still being able to render that to string and also not making major changes to the code for server side rendering...

Right now we are getting "Loading..." message and the string doesn't contain data which is loaded.

@paf31 Help is appreciated. Thanks in Advance!

Improve the Action monad

The Action monad should support:

  • Capture the current continuation
  • Set the component state
  • Lifting Eff actions

Question: combining components with different props

Assume I have two components, both stateless (for simplicity), both have their own props and actions:

-- widget.purs
widget :: Spec _ _ WidgetProps WidgetAction
widget = simpleSpec performWidgetAction renderWidget

Now I want to add the widget into some sort of a panel:

-- panel.purs   
panel :: Spec _ _ PanelProps PanelAction
panel = simpleSpec performPanelAction renderPanel

It is OK for a panel to "know" about the widget (and other components inside it), but I want the widget to be completely unaware of the panel so it can be composed into other things.

I also want the panel to "react" on what happens in the widget. Should it be handling WidgetAction or should WidgetProps declare some sort of a callback - I don't know what is be the better way, and it is also a part of my question.

Is there an example showing how to compose two components with different props and actions so one "subscribes" to the "events" of another?
Or what is the way to do it with purescript-react/thermite?

Regards,
Alexey.

[Question] Effects in Actions

Apologies if this is the wrong place to ask this, but I'm trying to figure out how to use Effects in the function for performAction in the source below

import Debug.Trace (trace, Trace(..))
import Control.Monad.Eff
import Control.Monad.Eff.Class

-- Thremite imports

data AppEv = AppDoNothing | AppSearch String
type AppPs = {}
type AppSt = {}
type AppEf = Eff (trace :: Trace)

performAction :: forall e. T.PerformAction AppPs AppEv (T.Action (AppEf _) AppSt)
performAction _ e = case e of 
  AppDoNothing -> do
    T.modifyState \_ -> {}
    return unit
  AppSearch ws -> do
    liftEff (trace ("searched " ++ ws))
    return unit

The compiler says

Error at /Users/Angus/code/web/wikit/client/src/Components/App.purs line 30, column 3 - line 31, column 3:
Error in declaration app
Cannot unify # ! with *.

Due to my lack of purescript knowledge, I'm having a little difficulty reading this, however a quick google tells me # ! is an effect kind or something, but It's still a little confusing for me.

When I change the type too

performAction :: T.PerformAction AppPs AppEv (T.Action _ AppSt)

I get the error message

No instance found for Control.Monad.Eff.Class.MonadEff (trace :: Debug.Trace.Trace | u21089) (Thermite.Action.Action u21073 {  })

So it seems that the Action type doesn't support Effects? Is this the result of my incompetence or is this by design?

ReactDOM failed to load

Opening the index.html file in the chromium browser throws the following error:

Uncaught TypeError: Cannot read property 'create' of undefined
    at PS.ReactDOM (index.js:2844)
    at index.js:2846

Monolithic components

Would it be feasible (unless it's design decision), for the Spec combining to somehow retain the components hierarchy (that is, not resulting in one monolithic component)?

That is, for example with focus:

spec1 :: Spec _ S1 _ A1
spec2 :: Spec _ S2 _ A2

spec :: Spec _ (Tuple S1 S2) _ (Either A1 A2)
spec = focus _1 _Left spec1 <> focus _2 _Right spec2

In my opinion, this should ideally result in a component having Tuple S1 S2 as state, passing S1 to 'left' component, S2 to 'right' component (as props), and the dispatch would left as it is (I think).

Now, why it should be done?

  • performance - I haven't measured it with Thermite, but I worked with an application where big component was resulting in bad performance. Sure there's virtual dom diffing, but the virtual copy of that DOM has to be rendered as a whole, whereas with smaller components one could use shouldComponentUpdate (and I believe it could be great with immutable data structures) to just optimize it out

  • tooling - Chrome' DevTools would probably offer better inspectability

Handling multiple states in event handlers

I am new to Purescript and Thermite, so please pardon me if the answer to this question is obvious. I would like to define a child component which has its own state and event handlers which it uses to validate its input. For example, take a simple text input. When we receive the onChange event from the DOM, we call our custom component action which validates the input and then updates its internal state accordingly (re-rendering itself to display a validation error). When the value is determined to be valid, I want the child component to call an event handler that it received from a parent component through its props. This way the valid value can then be passed back to the parent which can handle it accordingly.
However I am running into issues due to React's EventHandlerContext and Thermite's Action monad both being parameterized by component state.

Currently I can call the parent event handler function directly from the DOM event in the rendered child component (using React.Dom.Props.onChange), and since that function expects any kind of EventHandlerContext this works fine. However, if I want to run the child event handler first to validate the input, I get a type error since the parameterized state for each of the EventHandlerContext operations are different (child state vs parent state). So I can call the parent's event handler with the value, but I can't validate the value in the child component first.

If I choose to handle the onChange event usingThermite's PerformAction with a custom Action type in the child component, I can validate the input but I am unable to call the parent's event handler function for the same reason - the 2 states are different and so the two Thermite.Action operations have different state types. So in this case I can validate the value in the child but am unable to pass it to the parent's event handler.

Is there some way to accomplish both? I would like to encapsulate some simple validation into dumb child components so I can re-use them with different parent components. The parent does not need to worry about validation - it simply gets a valid input event or it gets no event at all, and the child handles all the validation with the user. Is there a simple way to accomplish this that I am overlooking?

State type lost by React

React seems to erase the class of the state object, resulting in failing pattern matches at runtime. For instance, if my state type is the following ADT:

data State = Success
           | Error String

Then, in the react implementation of setState, the new state will be assigned using assign({}, this._pendingState || this.state, partialState), which will erase javascript's type information about the state. As a result, the state object won't be an instance of Success or Error anymore but just an Object, resulting in an error when pattern matching on it.

To bypass this problem, I wrapped my state in another ADT (data State' = State' State), which seems to keep the type information of the state and it works then perfectly.

My solution is only a hack and it will probably be a problem for other people too, so we should either find a solution to this type erasure done by react, or we should put it big in the docs that this kind of things happen.

Thanks.

Uprade to PS 0.12 ?

Hi,
many thanks for this great library that is very useful for our project.

Since the 0.12 upgrade we were wondering if there was any plan(s) to upgrade purescript-thermite.

There is a branch created for that purpose with my collaborator already:
https://github.com/sudhirvkumar/purescript-thermite/tree/migrate_0_12

But before going deeper, we wanted to know if there was any plan(s) or advice(s) for such target.

Many thanks in advance for your answer.

Use of div' in render causes problems with react-native

Because there is no "div" component in react-native you get an error when using thermite.
I made a workaround which only uses a div if there is more than one element in the array but it would be better to make it customisable. I believe the equivalent in react-native would be to use the "view" component. Here is my work around for reference:

render :: React.Render props state eff
render this = map maybeDiv $
  spec.render (dispatcher this)
    <$> React.getProps this
    <*> React.readState this
    <*> React.getChildren this
    where
      maybeDiv elems = fromMaybe (div' elems) if (length elems == 1) then head elems else Nothing

Preview doesn't open sometimes

How to reproduce (only happens sometimes)

  1. click preview
  2. close preview
  3. click somewhere else
  4. click preview (window doesn't open)

Two state updates (cotransforms) in one performAction

In some cases two cotransforms in one performAction don't work, second is just ignored

For example insert T.cotransform id into ChangeCompleted action handler in test/Components/Task.purs:

  performAction (ChangeCompleted b)   _ _ = do
    T.cotransform id
    void (T.cotransform (_ { completed = b }))

And btw two cotransforms work fine in TaskList handlers

Documentation (tl;dr -- Types are not documentation)

Thanks so much for creating Purescript and its book. Both are truly fantastic.

I'm new to Purescript (with only 7 months of Haskell experience), so I'm not yet good enough to easily consume a Library like Thermite by following the types. (read: I've only recently grokked monad transformers) It's possible, but it's a bit like reading hieroglyphics. First I need to create a dictionary, then construct the rules of combination, and finally parse sentences.

As you know, documentation is a real problem in the Haskell community. And good, detailed documentation isn't on the horizon. However, we'd all benefit from the kind of minimal documentation provided by the Haskell prelude. (e.g., see below)

I believe that you could greatly advance adoption of Purescript by setting a standard for what constitutes minimally complete documentation, and encouraging the community to follow the standard.

The "types are documentation" argument is Just lazy hipster-speak for "I couldn't be Eff-ing bothered."


maybe :: b -> (a -> b) -> Maybe a -> b Source

The maybe function takes a default value, a function, and a Maybe value. If the Maybe value is Nothing, the function returns the default value. Otherwise, it applies the function to the value inside the Just and returns the result.

data Either a b Source

The Either type represents values with two possibilities: a value of type Either a b is either Left a or Right b.

The Either type is sometimes used to represent a value which is either correct or an error; by convention, the Left constructor is used to hold an error value and the Right constructor is used to hold a correct value (mnemonic: "right" also means "correct").

Support for inline styles

Hey, I hope I not just missed some way of doing this that already exists, but I'd like to use inline styles. This comes with the nice benefit of being able to express your styles in terms of code and running optimizations, dead-code-eliminations etc. on it.

In purescript-react the Style modifier accepts arbitrary records. Could the same be done here?

Use purescript-react

There is a lot of duplication now, and thermite could use purescript-react as a library instead.

Error when running `pulp test` after updating all required dependencies

Running pulp test after running bower update and npm install produces this error. It also happens in a project using purescript-thermite. purescript-react runs as expected.

c:\Users\Ulysses\Development\purescript-thermite\output\React.DOM\index.js:21
            return createElement(tag)(React_DOM_Props.unsafeFromPropsArray(props));
                   ^

TypeError: createElement is not a function
    at c:\Users\Ulysses\Development\purescript-thermite\output\React.DOM\index.js:21:20
    at Object.<anonymous> (c:\Users\Ulysses\Development\purescript-thermite\output\React.DOM\index.js:26:17)
    at Module._compile (module.js:398:26)
    at Object.Module._extensions..js (module.js:405:10)
    at Module.load (module.js:344:32)
    at Function.Module._load (module.js:301:12)
    at Module.require (module.js:354:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (c:\Users\Ulysses\Development\purescript-thermite\output\Thermite\index.js:22:17)
    at Module._compile (module.js:398:26)
* ERROR: Subcommand terminated with exit code 1

`createReactSpec` type too restrictive.

In my experience, having to supply the initial state without access to any effects while calling createReactSpec is a bit to restrictive. Given that the ReactSpec has a getInitialState field which allows effects, should't we allow the same opportunity here?

Multiple 'update' actions work on initial copy

I am quite new to thermite, so pardon me if this is easy - but I have tried to run multiple update in PerformAction and it seems to me it operates on the initial data, so earlier updates are lost. I just copied this example: https://github.com/nwolverson/purescript-thermite-example , changed the performAction to

performAction Increment _ _ update = do
    update $ \state -> state { counter = state.counter + 1 }
    update $ \state -> state { counter = state.counter + 3 }

And it is incrementing by 3, not by 4. Is this correct behaviour? Reading the source code somehow makes me think it shouldn't ignore the first update, but I don't have any experience with react.

Async actions

It would be useful to execute Actions in async. For example, you can do setStateAsync, but there is no way currently to obtain the state just before firing up the callback. You could of course pass in current state when starting up the async action, but it would be outdated and limits usefulness.

Am I missing something?

Firebug or the Firefox console lead to event loss when activated

I built a very simple page featuring an input field and a submit button. The code corresponding to that page is here.

Now the page seems to work correctly right after being loaded, but keeping the page open for a few minutes performance degrades, up to the point where keyboard events on the input get missed. For example, if i type abcdefghi on the keyboard, just acefhi would show up.

I asked for help about this in the chat, but nobody can reproduce the issue on that page. I am using Firefox 44.0.2 on Linux. I can see that the processor gets used a lot (almost one full core) by Firefox while i type in the input. I am not sure what am i doing wrong. First of all, it would be nice to find someone who can reproduce this. How do you suggest me to submit the test case?

React Mixins

I was wondering if the Mixin concept of React can be translated to typeclasses in Thermite.

The Documentation describes a Mixin to be a set of shared functions across multiple different components.

Could this be a way to write extensions to Thermite? I was especially thinking about a module that allows a component to produce its own Observable/Signal with purescript-rx / purescript-signal. Other common use cases would be adding Validation to Form Elements.

Maybe this route allows us to do something about #3 with some Javascript Magic. If you could point me into a direction I would love to go exploring and maybe provide a PR at the end.

Updating state in child components

Hi,

I am using the following lens-based approach to propagate a part of the state to the downstream components:

fold
   [ T.focus _items _ListItemAction $ T.foreach \_ -> listItemSpec
   , listActions
   ]

The first component renders a list of items, the second doesn't render anything but defines performAction.

When listItemSpec is updating the state the change is correctly propagated to the parent component through the lens, but the performAction in the second component (listActions) always receives the "old" state, the one "before" changes are applied through the _items lense.

How can a parent component update the state after it has been altered by its children?

Thanks.

Asynchronous updates overwrite entire application state

Imagine we have PerformActions similar to:

performA SlowEvent _ state _ update = timeoutSeconds 10 $ update $ state {foo = "blah"}

performB CountUp _ state _ update = update $ state {counter = state.counter + 1}

Say SlowEvent happens and then, before the 10 second timeout is finished, CountUp happens. Briefly we have a state where the counter has changed, but that change is eventually lost when SlowEvent completes. Even if performA and performB use focus to view different parts of the state we lose all updates in between an asynchronous call.

This example is contrived of course, but it isn't hard to imagine this happening in real applications that call JSON APIs to fetch data.

README: defaultMain params seem incorrect

Hello,

In the README, it seems that the parameters used for default main are incorrect:

main = defaultMain spec initialState "MyComponent" {}

On my machine the code doesn't compile (Type does not Unify).
Looking at the tests of thermite-dom, it seems that cons function is needed:
https://github.com/athanclark/purescript-thermite-dom/blob/master/test/Main.purs

Therefore, it should probably be:

main = defaultMain spec (cons initialState) "MyComponent" {}

This has solved the issue on my machine.

Can't get any examples working

I'm new to PureScript and Thermite. I'm running a fresh Arch install (I set it up yesterday).

I tried running the "Actions" lesson from Try Thermite by doing the following:

  • mkdir thermite-test && cd thermite-test
  • pulp init
  • bower i purescript-thermite --save
  • copying and pasting the code from that lesson into src/Main.purs
  • pulp build

but I get a huge bunch of errors, mostly to do with unexpected "qualified"statements in the dependencies like Data.Lazy.

I think this may have to do with some changes in PureScript syntax, since when I do the bower installation, I'm asked to choose between Prelude versions 0.1.5 and 1.0.0 or something similar because of a dependency conflict.

Any ideas?

Passing keys to child components

I have this issue https://fb.me/react-warning-keys when integrating existing react components (in jsx) with purescript ones.

I've rewritten my MessageListItem component to purescript+thermite and rendering it like this:

React = require("react")
MessageListItem = require("./MessageListItem")
MessageListItemPurs = require("./MessageListItemPurs").component

module.exports = React.createClass
  render: ->
    <div style={@style}>
      {@props.messages.map (message) ->
        <MessageListItemPurs key={message.id} {...message} />
      }
    </div>

  style:
    padding: 0
    display: "flex"
    flexFlow: "column nowrap"

and my MessageListItemPurs:

module MessageListItemPurs where

import Data.Date
import Data.Maybe
import qualified Thermite as T
import qualified Thermite.Html as T
import qualified Thermite.Html.Elements as T
import qualified Thermite.Html.Attributes as A
import qualified Thermite.Action as T
import qualified Thermite.Events as T
import qualified Thermite.Types as T

type Author = { login :: String }
type Props = { body :: String, author :: Author,  timestamp :: String}

render :: T.Render _ Unit Props Unit
render _ _ props _ = T.div' [counter]
  where
  counter :: T.Html _
  counter =
    T.div'
      [ T.div' [
          T.strong' [T.text props.author.login]
        , T.text " - "
        , date (fromString props.timestamp)
        ]
      ]

  date :: Maybe Date -> T.Html _
  date (Just d) = T.code' [T.text (show d)]
  date Nothing = T.text ""

spec :: T.Spec _ Unit Props Unit
spec = T.simpleSpec unit performAction render

component = T.createClass spec

performAction :: T.PerformAction _ Unit Props Unit
performAction _ _ = return unit

Tried adding key :: String to Props but it doesn't affect the compiled code in any way so that parameter doesn't get through to react?

Remove DOM dependency

Might it be better to have thermite depend solely on purescript-react, without purescript-react-dom and purescript-dom? This way, upgrading will be simpler and users of react native won't need to have the DOM stuff around.

Wanted to upgrade thermite to use purescript-react-5.0.0 when I ran into this.

Wishlist: example with webpack

This would make it easier to get started with thermite in an existing app. It could showcase a workflow for dealing with purescript packages with webpack. E.g. in my current set up, the app is crashing with:

17:42:16.453 TypeError: Prelude['<<<'] is not a function1 bundle.js:928:12

Cross-posted ethul/purs-loader#22

Routing and actions/effects while switching views

@paf31 Any plans or ideas for implementing Routing?

We are loving Thermite and have built a mobile app using react native and in the final stages of going live in the app store. Thanks for your hard work.

The only issue we are facing right now is we are having to have a single State which is shared with every single Spec. When we want to change the view. We need access to the whole State.

The reason being

  1. Need to set the value to the Current Page / View
  2. Load data necessary for the next view.

Any idea how to handle this?

We used the idea from your Slides https://github.com/paf31/codemesh2016/blob/master/thermite/src/Main.purs#L65

Once we figure this out... then we will be really happy as we will be able to build components which has State only relevant to itself instead of sharing the whole State across all Components.

Rough idea I have come up with is as follows.

  1. Define all the Routes as ADT and a function which runs initial effects when this function is called with the Route.
  2. FFI code with Route stored as a closure inside a function. Another function to change the Route and a list of subscribers.
  3. Setup a subscription in the main component which listens to the Route change. When the route changes dispatch an action. like your timer example. https://github.com/paf31/codemesh2016/blob/master/thermite/src/Main.purs#L755
  4. In this action we use the route and call the routing function which changes the Current Page/View and also runs the initial effects/actions for that route.
  5. In the Render function of any Spec, we should be able to call the changeRoute function which changes the route.

I hope my idea makes sense. If there is any simple way of handling routing. let me know!

This only solves the React Native issues... need to figure out the url change and route naccordingly. If this is implemented then it can be adapted to web too, like in purescript-pux

Help is appreciated. Thanks in advance!

Fix remaining warnings

Warning 1 of 7:

  in module Components.TaskList
  at purescript-thermite/test/Components/TaskList.purs line 110, column 3 - line 111, column 3

    Type variable 'eff' was shadowed.

  in value declaration taskList

  See https://github.com/purescript/purescript/wiki/Error-Code-ShadowedTypeVar for more information,
  or to contribute content related to this warning.

Warning 2 of 7:

  in module Components.TaskList
  at purescript-thermite/test/Components/TaskList.purs line 110, column 3 - line 111, column 3

    Type variable 'props' was shadowed.

  in value declaration taskList

  See https://github.com/purescript/purescript/wiki/Error-Code-ShadowedTypeVar for more information,
  or to contribute content related to this warning.

Warning 3 of 7:

  in module Components.TaskList
  at purescript-thermite/test/Components/TaskList.purs line 112, column 9 - line 113, column 9

    Wildcard type definition has the inferred type

      Eff ( refs :: ReactRefs ( read :: Read
                              )
          , state :: ReactState ( write :: Write
                                , read :: Read
                                )
          , props :: ReactProps
          | _52
          )
      Unit


  in value declaration taskList

  See https://github.com/purescript/purescript/wiki/Error-Code-WildcardInferredType for more information,
  or to contribute content related to this warning.

Warning 4 of 7:

  in module Components.TaskList
  at purescript-thermite/test/Components/TaskList.purs line 72, column 3 - line 73, column 3

    Type variable 'eff' was shadowed.

  in value declaration taskList

  See https://github.com/purescript/purescript/wiki/Error-Code-ShadowedTypeVar for more information,
  or to contribute content related to this warning.

Warning 5 of 7:

  in module Components.TaskList
  at purescript-thermite/test/Components/TaskList.purs line 72, column 3 - line 73, column 3

    Type variable 'props' was shadowed.

  in value declaration taskList

  See https://github.com/purescript/purescript/wiki/Error-Code-ShadowedTypeVar for more information,
  or to contribute content related to this warning.

Warning 6 of 7:

  in module Components.TaskList
  at purescript-thermite/test/Components/TaskList.purs line 99, column 3 - line 100, column 3

    Type variable 'eff' was shadowed.

  in value declaration taskList

  See https://github.com/purescript/purescript/wiki/Error-Code-ShadowedTypeVar for more information,
  or to contribute content related to this warning.

Warning 7 of 7:

  in module Components.TaskList
  at purescript-thermite/test/Components/TaskList.purs line 99, column 3 - line 100, column 3

    Type variable 'props' was shadowed.

  in value declaration taskList

  See https://github.com/purescript/purescript/wiki/Error-Code-ShadowedTypeVar for more information,
  or to contribute content related to this warning.

Asynchronous handlers closing over state?

Hi!

So I've fired Try Purescript! with Async lesson, to just have the general idea what Thermite provides in terms of asynchronous handlers: http://try.purescript.org/?gist=e6a2142e872dbc7e6557a78a55f03589&backend=thermite&session=68d15d4b-2a9b-f16d-2c1e-1a6e932736fa

The original example is rather simple, so I wanted to add something more to make it more "real world":

performAction :: T.PerformAction _ State _ Action
performAction Increment _ _ = void do
  T.modifyState \state -> state + 10
  lift (delay 500)
  T.modifyState \state -> state + 1
performAction Decrement _ _ = void do
  lift (delay 500)
  T.modifyState \state -> state - 1

This emulates the common pattern in which I'd set some flag in the state denoting in progress, then perform a request, and when response arrives I'd reset that flag.

With this example, when clicking fast enough, it's visible that the value is first incremented by 10, then goes back by 10, it's incremented by 1, goes back again, and after a while of such changes, it may arrive at non-correct value.

Missing hello world / template?

The "hello world" explained on the README doesn't compile as it seems to be missing an import to body. The examples at Try Thermite depend on Thermite.Try, so, I think they're not supposed to be used as a template for a new project too. What is a proper Thermite Hello World that I can use as a template for new Thermite projects?

What is R.p' in the Getting Started Documentation?

I entered the example code from the Getting Started guide (top level README.md) into a new file. Around line 20, there is a statement like this:

render :: T.Render State _ Action
render dispatch _ state =
    [ R.p' [ R.text "Value: "
            , R.text $ show state.counter
            ]
    , R.p'  [ R.button  [RP.onClick \_ -> dispatch Increment ]
                        [ R.text "Increment" ]
            , R.button  [RP.onClick \_ -> dispatch Decrement ]
                        [ R.text "Decrement" ]
            ]
  ]

R.p' results in an unknown symbol error. Obviously this is supposed to be replaced by something else, but what?

API for dispatching actions into elements?

Hi,

I'm currently combining purescript-routing and thermite with a bit of code like the following to dispatch events into the root component:

componentDidMount :: forall props state eff. (React.ReactThis props state -> InputAction -> T.EventHandler) -> R.ComponentDidMount props state (console :: CONSOLE | eff)
componentDidMount dispatch this = do
    matches routing callback
  where
    callback :: Maybe Routes -> Routes -> T.EventHandler
    callback _ RouteA = do
      dispatch this ActionA
    callback _ RouteB = do
      dispatch this ActionB

This works but isn't particularly beautiful. I was wondering if you have an idea / interest for how to add a halogen driver (https://github.com/slamdata/purescript-halogen/blob/v0.5.14/docs/Halogen/Driver.md) like function?

Default implementation for shouldComponentUpdate?

shouldComponentUpdate can be used to examine the incoming properties and state and decide whether the component should be re-rendered. Given purescript's immutability, we can implement this in the bindings using reference equality similar to this react mixin.

To accommodate edge cases that require mutability, it probably makes sense to include this as part of the Spec and add the reference checking as a Just default for simpleSpec. Thoughts?

Nesting Specs

Thermite has the concept of sequencing Specs via the Semigroup instance, but sometimes you want to nest the markup of a Spec inside another. Lately I've been using this helper function:

wrap :: forall eff state props action.
    Spec eff state props action ->
    Spec eff state props action ->
    Spec eff state props action
wrap parent child = simpleSpec pa rn 
 where
    pa a p st k = do
        view _performAction parent a p st k
        view _performAction child a p st k
    rn k p st children = view _render parent k p st (view _render child k p st children)

At this point, I'm not sure whether this re-appropriation of the children argument is elegant or hacky. Does it make sense to add a function like this to Thermite? I'm definitely open to better names as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.