|
8 | 8 | please copy Router v1 into your own codebase.
|
9 | 9 | Maintaining two Routers in scalajs-react is not good.
|
10 | 10 |
|
| 11 | +* [#145](https://github.com/japgolly/scalajs-react/issues/145): Unify `-->` `==>` `~~>`, remove Scalaz `IO`, add `Callback`. |
| 12 | + |
| 13 | + **Problem:**<br> |
| 14 | + A gap in the community and library was growing because there were two separate ways of writing callbacks. |
| 15 | + An example of which, numerous users using the built-in `Router` experienced confusion and were forced to |
| 16 | + discover Scalaz's (and Haskell's) `unsafePerformIO()` without understanding the reasons or benefit. |
| 17 | + Another example, writing mixins or shared libraries, one would have to create duplicate methods with differing |
| 18 | + type signatures in order to accomodate both styles. |
| 19 | + More examples exist, but it should be clear that this please-everyone approach has failed. |
| 20 | + |
| 21 | + **Solution:**<br> |
| 22 | + A new data type `Callback` has been added |
| 23 | + (which is actually an alias to the real type `CallbackTo[A]` where `Callback = CallbackTo[Unit]`). |
| 24 | + It replaces the existing two competing methods of `() => Unit` and Scalaz `IO[Unit]`. |
| 25 | + A basic rule of thumb is if a function performs an effect (changing DOM, using network, changing React state), |
| 26 | + it should be wrapped in a `Callback`. |
| 27 | + |
| 28 | + * When creating HTML, `-->` and `==>` only accept `Callback`s. |
| 29 | + * `setState()`, `modState()` etc now return `Callback`s. |
| 30 | + * Core now has `_setState()`, `_modState()`. |
| 31 | + * Component lifecycle methods (like `componentWillMount`) now accept `Callback`. |
| 32 | + * `Router` no longer uses Scalaz `IO`. |
| 33 | + * Mixins in `extra` no longer have duplicate methods for Scalaz `IO`. |
| 34 | + |
| 35 | + For Scalaz users:<br> |
| 36 | + Either use `CallbackTo` instead of `IO`, or else change your `IO`s to `CallbackTo`s when you pass them to React. |
| 37 | + * `~~>` is removed. Use `-->` and `==>` instead. |
| 38 | + * `{,_}{set,mod}StateIO` methods all removed and in core without the `IO`. |
| 39 | + * Isomorphism between `IO` and `CallbackTo` with convenience extension methods `.toCallback` and `.toIO`. |
| 40 | + * `OpCallbackIO` has been removed in favour of plain `Callback`. |
| 41 | + * New state-monad type-inference convenience: `FixCB[S] = FixT[CallbackTo, S]` |
| 42 | + |
| 43 | + **Example migrations**<br> |
| 44 | + |
| 45 | + Example #1 |
| 46 | + ```scala |
| 47 | + // Before |
| 48 | + val Example = ReactComponentB[Unit]("Example") |
| 49 | + .initialState(0) |
| 50 | + .render { $ => |
| 51 | + |
| 52 | + def clickHandler = $.modState(_ + 1) |
| 53 | + <.div( |
| 54 | + <.div("Button pressed ", $.state, " times."), |
| 55 | + <.button("CLICK ME", ^.onClick --> clickHandler)) |
| 56 | + |
| 57 | + }.buildU |
| 58 | + |
| 59 | + // After |
| 60 | + // Surprise! No change needed. |
| 61 | + // modState() now returns a Callback which is exactly what --> expects. |
| 62 | + ``` |
| 63 | + |
| 64 | + Example #2<br> |
| 65 | + ```scala |
| 66 | + // Before (excerpt from example above) |
| 67 | + def clickHandler: Unit = { |
| 68 | + println("Updating state.") |
| 69 | + $.modState(_ + 1) |
| 70 | + } |
| 71 | + |
| 72 | + // Callbacks can compose in many different ways. |
| 73 | + // The following examples all do the same thing in the same order. |
| 74 | + |
| 75 | + // After, Method #1: precedeWith |
| 76 | + def clickHandler: Callback = |
| 77 | + $.modState(_ + 1).precedeWith { |
| 78 | + println("Updating state.") |
| 79 | + } |
| 80 | + |
| 81 | + // After, Method #2: >> |
| 82 | + def clickHandler: Callback = |
| 83 | + Callback(println("Updating state.")) >> |
| 84 | + $.modState(_ + 1) |
| 85 | + |
| 86 | + // After, Method #3: << |
| 87 | + def clickHandler: Callback = |
| 88 | + $.modState(_ + 1) << Callback(println("Updating state.")) |
| 89 | + |
| 90 | + // After, Method #4: runNow() |
| 91 | + def clickHandler = Callback { |
| 92 | + println("Updating state.") |
| 93 | + $.modState(_ + 1).runNow() |
| 94 | + } |
| 95 | + ``` |
| 96 | + |
| 97 | + |
11 | 98 | <br>
|
12 | 99 | Migration commands:
|
13 | 100 | ```sh
|
14 | 101 | # extra.{router2 ⇒ router}
|
15 | 102 | find . -name '*.scala' -type f -exec perl -pi -e 's/(?<=extra\.router)2//g' {} +
|
| 103 | + |
| 104 | +# Unfortunately the migration to use Callback is mostly manual. |
| 105 | +# Here are some commands I used to help but they'll only get you halfway there. |
| 106 | +find . -name '*.scala' -type f -exec perl -pi -e 's/(?<=forceUpdate)\(\)//g' {} + |
| 107 | +find . -name '*.scala' -type f -exec perl -pi -e 's/(?<=tryFocus)\(\)//g' {} + |
| 108 | +find . -name '*.scala' -type f -exec perl -pi -e 's/(?<=(set|mod)State)IO//g' {} + |
| 109 | +find . -name '*.scala' -type f -exec perl -pi -e 's/FixT\[IO *, */FixCB[/' {} + |
| 110 | +find . -name '*.scala' -type f -exec perl -pi -e 's/ReactST\[IO *,/ReactST[CallbackTo,/' {} + |
16 | 111 | ```
|
0 commit comments