Skip to content

Additional documentation #143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 14, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ For a more high-level set of bindings, you might like to look at `purescript-the
bower install purescript-react
```

This library requires the `react` module. This dependency may be satisfied by installing the NPM [react package](https://www.npmjs.com/package/react).

```
npm install react
```

## Related Modules

- [React DOM](https://github.com/purescript-contrib/purescript-react-dom)
Expand All @@ -22,3 +28,189 @@ bower install purescript-react
## Example

Please refer to [purescript-react-example](https://github.com/ethul/purescript-react-example)

## Troubleshooting

#### How to use JavaScript components?

To use a React component that is published as a JavaScript module, one
can leverage PureScript's FFI to define a type for the component and its
props. Consider the following example.

```purescript
module Clock (clockComponent) where

import React (ReactClass, SyntheticEventHandler, Children)
import React.SyntheticEvent (SyntheticEvent)

foreign import clockComponent
:: ReactClass
{ children :: Children
, format :: String
, className :: String
, onTick :: SyntheticEventHandler SyntheticEvent
}
```

Rendering the `clockComponent` can be done as follows.

```purescript
module Component where

import Prelude

import React as React

import Clock as Clock

clock :: React.ReactElement
clock =
React.createElement Clock.clockComponent
{ format: "HH:mm:ss"
, className: "test-class-name"
, onTick: React.handle $ \event -> do
React.preventDefault event
-- etc.
pure unit
} []
```

A consideration when defining a type for an external component is that
some components pass their props through to a DOM element. In a case
such as this, it can be helpful to leverage the props defined in the
`React.DOM.Props` module. One way to accomplish this is to define the
external component as follows.

```purescript
module Clock
( clockComponent
, format
, onTick
) where

import Prelude

import React (ReactClass, ReactElement, SyntheticEventHandlerContext, Children, createElement, handle)
import React.SyntheticEvent (SyntheticEvent)
import React.DOM.Props (Props, unsafeFromPropsArray, unsafeMkProps)

clockComponent :: Array Props -> Array ReactElement -> ReactElement
clockComponent props children = createElement clockComponent_ (unsafeFromPropsArray props :: {}) children

format :: String -> Props
format = unsafeMkProps "format"

onTick :: forall eff props state. (SyntheticEvent -> SyntheticEventHandlerContext eff props state Unit) -> Props
onTick k = unsafeMkProps "onTick" (handle k)

foreign import clockComponent_
:: ReactClass
{ children :: Children
}
```

Rendering the `clockComponent` can be done as follows.

```purescript
module Component where

import Prelude

import React as React
import React.DOM.Props as Props

import Clock as Clock

clock :: React.ReactElement
clock =
Clock.clockComponent
[ Clock.format "HH:mm:ss"
, Clock.onTick $ \event -> do
React.preventDefault event
-- etc.
pure unit
, Props.className "test-class-name"
, Props.style
{ fontWeight: "bold"
, color: "blue"
}
-- additional Props.*
]
[ ]
```

#### Components with type class constraints re-mount on every render?

Consider the following example where an ordered list component is
defined for any item of type `a`, where `a` is constrained to have an
`Ord` type class instance.

```purescript
module OrderedList where

import Prelude

import Data.Array (sort)

import React as React
import React.DOM as DOM
import Debug.Trace as Trace

type OrderedListProps a
= { items :: Array a
, renderItem :: a -> React.ReactElement
}

orderedList :: forall a. Ord a => React.ReactClass (OrderedListProps a)
orderedList = React.component "OrderedList" component
where
component this =
pure { state: {}
, componentDidMount: do
_ <- pure $ Trace.spy "OrderedList.componentDidMount"
pure unit
, render: render <$> React.getProps this
}
where
render
{ items
, renderItem
} =
DOM.ol [ ] $
renderItem' <$> sort items
where
renderItem' a =
DOM.li
[ ]
[ renderItem a ]

-- This would be defined where the type parameter `a` is known.

orderedListInt :: React.ReactClass (OrderedListProps Int)
orderedListInt = orderedList
```

If the component `orderedList` above were to be rendered, the debugging
statement `OrderedList.componentDidMount` is printed to the console each
time the parent component is rendered. The reason for this is due to how
the `orderedList` component is compiled to JavaScript.

```javascript
var orderedList = function (dictOrd) {
var component = function ($$this) {
// ...
};
return React.component()("OrderedList")(component);
};
```

Above, the component creation is wrapped by the function with the
`dictOrd` parameter. This means that a new component is being created on
each render of the component using `orderedList`. This may not be ideal
in all cases; e.g., if `orderedList` had needed to store state.

To avoid `orderedList` from being recreated each time, a function can be
defined that specifies the type parameter with the type class contraint.
If the component using the ordered list knows that the items are of type
`Int`, the component can define `orderedListInt` as shown above, and use
it to render the ordered list instead of `orderedList`.