Skip to content

Commit 3c14c3c

Browse files
Removed dependency on create-react-class, fixed #5
1 parent 136c318 commit 3c14c3c

File tree

6 files changed

+128
-75
lines changed

6 files changed

+128
-75
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
Develop React applications with Scala.
99

10-
It is compatible with Scala `2.12`, `2.13` and Scala.js `0.6.29`, `1.1.0`
10+
It is compatible with Scala `2.12`, `2.13`, Scala.js `0.6.29`, `1.1.0` and with React.js `v16.8+`.
1111

1212
Optionally include `react-router` and `react-redux` facades, too.
1313

@@ -45,13 +45,13 @@ ReactDOM.render(<.div(^.id := "hello-world")("Hello, World!"), mountNode)
4545
2. Depend on the libraries.
4646
```
4747
libraryDependencies ++= Seq(
48-
"org.scommons.shogowada" %%% "scalajs-reactjs-core" % "0.15.0", // For react facade
49-
"org.scommons.shogowada" %%% "scalajs-reactjs-dom" % "0.15.0", // For react-dom facade
48+
"org.scommons.shogowada" %%% "scalajs-reactjs-core" % "0.16.0", // For react facade
49+
"org.scommons.shogowada" %%% "scalajs-reactjs-dom" % "0.16.0", // For react-dom facade
5050
51-
"org.scommons.shogowada" %%% "scalajs-reactjs-router-dom" % "0.15.0", // Optional. For react-router-dom facade
52-
"org.scommons.shogowada" %%% "scalajs-reactjs-router-redux" % "0.15.0", // Optional. For react-router-redux facade
53-
"org.scommons.shogowada" %%% "scalajs-reactjs-redux" % "0.15.0", // Optional. For react-redux facade
54-
"org.scommons.shogowada" %%% "scalajs-reactjs-redux-devtools" % "0.15.0" // Optional. For redux-devtools facade
51+
"org.scommons.shogowada" %%% "scalajs-reactjs-router-dom" % "0.16.0", // Optional. For react-router-dom facade
52+
"org.scommons.shogowada" %%% "scalajs-reactjs-router-redux" % "0.16.0", // Optional. For react-router-redux facade
53+
"org.scommons.shogowada" %%% "scalajs-reactjs-redux" % "0.16.0", // Optional. For react-redux facade
54+
"org.scommons.shogowada" %%% "scalajs-reactjs-redux-devtools" % "0.16.0" // Optional. For redux-devtools facade
5555
)
5656
```
5757

build.sbt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
val CreateReactClassVersion = "^15.5.1"
21
val HistoryVersion = "^4.6.1"
32
val ReactVersion = "^16.8.0"
43
val ReactReduxVersion = "^5.0.3"
@@ -97,7 +96,6 @@ lazy val core = project.in(file("core"))
9796
"org.scommons.shogowada" %%% "statictags" % StaticTagsVersion
9897
),
9998
npmDependencies in Compile ++= Seq(
100-
"create-react-class" -> CreateReactClassVersion,
10199
"react" -> ReactVersion
102100
),
103101
(webpack in(Compile, fastOptJS)) := Seq(),
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const React = require('react');
2+
3+
module.exports = {
4+
5+
create: function (
6+
displayName,
7+
renderDef,
8+
getInitialState,
9+
componentDidMountDef,
10+
shouldComponentUpdateDef,
11+
componentDidUpdateDef,
12+
componentWillUnmountDef,
13+
componentDidCatchDef) {
14+
15+
class ReactComponentImpl extends React.Component {
16+
constructor(props) {
17+
super(props);
18+
this.state = getInitialState.call(this);
19+
}
20+
21+
componentDidMount() {
22+
componentDidMountDef.call(this);
23+
}
24+
25+
shouldComponentUpdate(nextProps, nextState) {
26+
return shouldComponentUpdateDef.call(this, nextProps, nextState);
27+
}
28+
29+
componentDidUpdate(prevProps, prevState) {
30+
componentDidUpdateDef.call(this, prevProps, prevState);
31+
}
32+
33+
componentWillUnmount() {
34+
componentWillUnmountDef.call(this);
35+
}
36+
37+
render() {
38+
return renderDef.call(this);
39+
}
40+
};
41+
42+
if (displayName) {
43+
ReactComponentImpl.displayName = displayName;
44+
ReactComponentImpl.toString = function () {
45+
return displayName;
46+
};
47+
}
48+
49+
if (componentDidCatchDef) {
50+
51+
class ErrorBoundaryImpl extends ReactComponentImpl {
52+
53+
componentDidCatch(error, info) {
54+
componentDidCatchDef.call(this, error, info);
55+
}
56+
};
57+
58+
return ErrorBoundaryImpl;
59+
}
60+
61+
return ReactComponentImpl;
62+
}
63+
};

core/src/main/scala/io/github/shogowada/scalajs/reactjs/React.scala

Lines changed: 50 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -63,76 +63,59 @@ object React {
6363
def createClass[WrappedProps, State](
6464
render: Self[WrappedProps, State] => ReactElement,
6565
displayName: String = null,
66-
componentWillMount: Self[WrappedProps, State] => Unit = null,
6766
componentDidMount: Self[WrappedProps, State] => Unit = null,
68-
componentWillReceiveProps: (Self[WrappedProps, State], Props[WrappedProps]) => Unit = null,
6967
shouldComponentUpdate: (Self[WrappedProps, State], Props[WrappedProps], State) => Boolean =
7068
(self: Self[WrappedProps, State], nextProps: Props[WrappedProps], nextState: State) => {
7169
self.props.wrapped != nextProps.wrapped || self.state != nextState ||
7270
!Utils.shallowEqual(self.props.native, nextProps.native, WrappedProperty)
7371
},
74-
componentWillUpdate: (Self[WrappedProps, State], Props[WrappedProps], State) => Unit = null,
7572
componentDidUpdate: (Self[WrappedProps, State], Props[WrappedProps], State) => Unit = null,
7673
componentWillUnmount: Self[WrappedProps, State] => Unit = null,
74+
componentDidCatch: (Self[WrappedProps, State], js.Object, js.Dynamic) => Unit = null,
7775
getInitialState: Self[WrappedProps, State] => State = null
7876
): ReactClass = {
79-
val spec = js.Dynamic.literal(
80-
"shouldComponentUpdate" -> js.ThisFunction.fromFunction3((native: js.Dynamic, nextProps: js.Dynamic, nextState: js.Dynamic) => {
81-
shouldComponentUpdate(Self(native), Props(nextProps), stateFromNative(nextState))
77+
NativeCreateClass.create(
78+
displayName = displayName,
79+
render = js.ThisFunction.fromFunction1((native: js.Dynamic) => {
80+
render(Self(native))
8281
}),
83-
"getInitialState" -> js.ThisFunction.fromFunction1((native: js.Dynamic) => {
82+
getInitialState = js.ThisFunction.fromFunction1((native: js.Dynamic) => {
8483
if (getInitialState != null) {
85-
stateToNative(getInitialState(Self(native)))
84+
React.stateToNative(getInitialState(Self(native)))
8685
} else {
87-
stateToNative(())
86+
React.stateToNative(())
8887
}
8988
}),
90-
"render" -> js.ThisFunction.fromFunction1((native: js.Dynamic) => {
91-
render(Self(native))
92-
})
89+
componentDidMount = js.ThisFunction.fromFunction1((native: js.Dynamic) => {
90+
if (componentDidMount != null) {
91+
componentDidMount(Self(native))
92+
}
93+
}),
94+
shouldComponentUpdate = js.ThisFunction.fromFunction3((native: js.Dynamic, nextProps: js.Dynamic, nextState: js.Dynamic) => {
95+
if (shouldComponentUpdate != null) {
96+
shouldComponentUpdate(Self(native), Props(nextProps), React.stateFromNative(nextState))
97+
}
98+
else true
99+
}),
100+
componentDidUpdate = js.ThisFunction.fromFunction3((native: js.Dynamic, prevProps: js.Dynamic, prevState: js.Dynamic) => {
101+
if (componentDidUpdate != null) {
102+
componentDidUpdate(Self(native), Props(prevProps), React.stateFromNative(prevState))
103+
}
104+
}),
105+
componentWillUnmount = js.ThisFunction.fromFunction1((native: js.Dynamic) => {
106+
if (componentWillUnmount != null) {
107+
componentWillUnmount(Self(native))
108+
}
109+
}),
110+
componentDidCatch = {
111+
if (componentDidCatch != null) {
112+
js.ThisFunction.fromFunction3((native: js.Dynamic, error: js.Object, info: js.Dynamic) => {
113+
componentDidCatch(Self(native), error, info)
114+
})
115+
}
116+
else null
117+
}
93118
)
94-
95-
if (displayName != null) {
96-
spec.updateDynamic("displayName")(displayName)
97-
}
98-
99-
if (componentWillMount != null) {
100-
spec.updateDynamic("componentWillMount")(js.ThisFunction.fromFunction1((native: js.Dynamic) => {
101-
componentWillMount(Self(native))
102-
}))
103-
}
104-
105-
if (componentDidMount != null) {
106-
spec.updateDynamic("componentDidMount")(js.ThisFunction.fromFunction1((native: js.Dynamic) => {
107-
componentDidMount(Self(native))
108-
}))
109-
}
110-
111-
if (componentWillReceiveProps != null) {
112-
spec.updateDynamic("componentWillReceiveProps")(js.ThisFunction.fromFunction2((native: js.Dynamic, nextProps: js.Dynamic) => {
113-
componentWillReceiveProps(Self(native), Props(nextProps))
114-
}))
115-
}
116-
117-
if (componentWillUpdate != null) {
118-
spec.updateDynamic("componentWillUpdate")(js.ThisFunction.fromFunction3((native: js.Dynamic, nextProps: js.Dynamic, nextState: js.Dynamic) => {
119-
componentWillUpdate(Self(native), Props(nextProps), stateFromNative(nextState))
120-
}))
121-
}
122-
123-
if (componentDidUpdate != null) {
124-
spec.updateDynamic("componentDidUpdate")(js.ThisFunction.fromFunction3((native: js.Dynamic, prevProps: js.Dynamic, prevState: js.Dynamic) => {
125-
componentDidUpdate(Self(native), Props(prevProps), stateFromNative(prevState))
126-
}))
127-
}
128-
129-
if (componentWillUnmount != null) {
130-
spec.updateDynamic("componentWillUnmount")(js.ThisFunction.fromFunction1((native: js.Dynamic) => {
131-
componentWillUnmount(Self(native))
132-
}))
133-
}
134-
135-
NativeCreateReactClass(spec)
136119
}
137120

138121
def createElement(tagName: String, attributes: js.Any, children: js.Any*): ReactElement =
@@ -156,7 +139,17 @@ object NativeReact extends js.Object {
156139
}
157140

158141
@js.native
159-
@JSImport("create-react-class", JSImport.Default)
160-
object NativeCreateReactClass extends js.Object {
161-
def apply(spec: js.Any): ReactClass = js.native
142+
@JSImport("./io/github/shogowada/scalajs/reactjs/CreateClass.js", JSImport.Namespace)
143+
object NativeCreateClass extends js.Object {
144+
145+
def create(
146+
displayName: String,
147+
render: js.ThisFunction0[js.Dynamic, ReactElement],
148+
getInitialState: js.ThisFunction0[js.Dynamic, js.Dynamic],
149+
componentDidMount: js.ThisFunction0[js.Dynamic, Unit],
150+
shouldComponentUpdate: js.ThisFunction2[js.Dynamic, js.Dynamic, js.Dynamic, Boolean],
151+
componentDidUpdate: js.ThisFunction2[js.Dynamic, js.Dynamic, js.Dynamic, Unit],
152+
componentWillUnmount: js.ThisFunction0[js.Dynamic, Unit],
153+
componentDidCatch: js.ThisFunction2[js.Dynamic, js.Object, js.Dynamic, Unit]
154+
): ReactClass = js.native
162155
}

example/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ In addition to `render` function, it also supports [all the functions `React.Com
4848

4949
The first argument of each function must be `Self[WrappedProps, State]`, props must have type `Props[WrappedProps]`, and states must have type `State`.
5050

51-
For example, [`componentWillUpdate(nextProps, nextState)` function](https://facebook.github.io/react/docs/react-component.html#componentwillupdate) will be `componentWillUpdate(self: Self[WrappedProps, State], nextProps: Props[WrappedProps], nextState: State): Unit`:
51+
For example, [`shouldComponentUpdate(nextProps, nextState)` function](https://reactjs.org/docs/react-component.html#shouldcomponentupdate) will be `shouldComponentUpdate(self: Self[WrappedProps, State], nextProps: Props[WrappedProps], nextState: State): Boolean`:
5252

5353
```scala
5454
case class WrappedProps(/* ... */)
@@ -58,7 +58,7 @@ type Self = React.Self[WrappedProps, State]
5858
type Props = React.Props[WrappedProps]
5959

6060
val reactClass: ReactClass = React.createClass[WrappedProps, State](
61-
componentWillUpdate = (self: Self, nextProps: Props, nextState: State) => {/* do something */},
61+
shouldComponentUpdate = (self: Self, nextProps: Props, nextState: State) => {/* do something */},
6262
render = (self: Self) => <.div()("Hello, World!")
6363
)
6464
```
@@ -89,6 +89,9 @@ ReactDOM.render(
8989
While many want to use case classes as props, React requires props to be a plain JavaScript object. So, to use case classes, we need to wrap them in another property. In this facade, we wrap them in "wrapped" property.
9090

9191
```scala
92+
import io.github.shogowada.scalajs.reactjs.ReactDom
93+
import io.github.shogowada.scalajs.reactjs.VirtualDOM._
94+
9295
case class WrappedProps(foo: String, bar: Int)
9396

9497
val reactClass = React.createClass[WrappedProps, Unit](
@@ -122,6 +125,9 @@ You can extend it as you see needs. See [`RouterProps`](/router/src/main/scala/i
122125
States are wrapped and unwrapped automatically, so you don't need to do `state.wrapped`. We can wrap and unwrap states automatically because nothing extends states; it is supposed to be local to each component.
123126

124127
```scala
128+
import io.github.shogowada.scalajs.reactjs.ReactDomVirtualDOM._
129+
import io.github.shogowada.scalajs.reactjs.VirtualDOM._
130+
125131
case class State(text: String)
126132

127133
val reactClass = React.createClass[Unit, State](

example/lifecycle/src/main/scala/io/github/shogowada/scalajs/reactjs/example/lifecycle/Main.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ object App {
1717
def apply() = reactClass
1818

1919
private lazy val reactClass = React.createClass[Unit, State](
20-
componentWillMount = (self) => println("componentWillMount()"),
2120
componentDidMount = (self) => {
2221
println("componentDidMount()")
2322
self.setState(_.copy(componentDidMountCalled = true))
@@ -26,12 +25,6 @@ object App {
2625
println(s"shouldComponentUpdate($nextProps, $nextState)")
2726
nextState != self.state
2827
},
29-
componentWillReceiveProps = (self, nextProps) => {
30-
println(s"componentWillReceiveProps($nextProps)")
31-
},
32-
componentWillUpdate = (self, nextProps, nextState) => {
33-
println(s"componentWillUpdate($nextProps, $nextState)")
34-
},
3528
componentDidUpdate = (self, prevProps, prevState) => {
3629
println(s"componentDidUpdate($prevProps, $prevState)")
3730
self.setState(_.copy(componentDidUpdateCalled = true))

0 commit comments

Comments
 (0)