Skip to content
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

React Hooks v16.8 #121

Merged
merged 3 commits into from
Jul 22, 2019
Merged
Show file tree
Hide file tree
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
44 changes: 44 additions & 0 deletions kotlin-react/src/main/kotlin/react/Imports.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,47 @@ external fun <P : RProps> lazy(loadComponent: () -> Promise<RClass<P>>): RClass<
// Suspense (16.6+)
external interface SuspenseProps : RProps
external val Suspense: RClass<SuspenseProps>

// State Hook
@JsName("useState")
external fun <T> rawUseState(initValue: T): RDependenciesArray

// Reducer Hook
@JsName("useReducer")
external fun <S, A> rawUseReducer(reducer: RReducer<S, A>, initialState: S): RDependenciesArray
@JsName("useReducer")
external fun <S, A> rawUseReducer(reducer: RReducer<S, A>, initialState: S, initialAction: A): RDependenciesArray
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use initialAction: A = definedExternally and avoid duplicating the function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't sure that it's equal in converted js. And this "optimisation" is unnecessary.


// Effect Hook
@JsName("useEffect")
external fun rawUseEffect(effect: () -> dynamic, dependencies: RDependenciesArray)
@JsName("useEffect")
external fun rawUseEffect(effect: () -> dynamic)

// Layout Effect Hook
@JsName("useLayoutEffect")
external fun rawUseLayoutEffect(effect: () -> dynamic, dependencies: RDependenciesArray)
@JsName("useLayoutEffect")
external fun rawUseLayoutEffect(effect: () -> dynamic)

// Context Hook
external fun <T> useContext(context: RContext<T>): T

// Callback Hook
external fun useCallback(callback: () -> Unit, dependencies: RDependenciesArray): () -> Unit

// Memo Hook
external fun <T> useMemo(callback: () -> T, dependencies: RDependenciesArray): T

// Ref Hook
external interface RMutableRef<T> : RRef {
var current: T
}
external fun <T> useRef(initialValue: T): RMutableRef<T>

// Imperative Methods Hook
external fun useImperativeHandle(ref: RRef, createInstance: () -> dynamic, inputs: RDependenciesArray): Unit

// Debug Value Hook
external fun useDebugValue(value: Any)
external fun <T> useDebugValue(value: T, format: (value: T) -> Any)
26 changes: 26 additions & 0 deletions kotlin-react/src/main/kotlin/react/RBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,29 @@ fun <P : RProps> forwardRef(handler: RBuilder.(RProps, RRef) -> Unit): RClass<P>
buildElements { handler(props, ref) }
}
}

typealias FunctionalComponent<P> = (props: P) -> dynamic

/**
* Get functional component from [func]
*/
fun <P : RProps> functionalComponent(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func: RBuilder.(props: P) -> Unit
): FunctionalComponent<P> {
return { props: P ->
buildElements {
func(props)
}
}
}

/**
* Append functional component [functionalComponent] as child of current builder
*/
fun <P : RProps> RBuilder.child(
functionalComponent: FunctionalComponent<P>,
props: P = jsObject {},
handler: RHandler<P> = {}
): ReactElement {
return child(functionalComponent, props, handler)
}
71 changes: 71 additions & 0 deletions kotlin-react/src/main/kotlin/react/hooks.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package react

typealias RDependenciesArray = Array<dynamic>
typealias RDependenciesList = List<dynamic>

typealias RSetState<T> = (value: T) -> Unit
fun <T> useState(initValue: T): Pair<T, RSetState<T>> {
val jsTuple = rawUseState(initValue)
val currentValue = jsTuple[0] as T
val setState = jsTuple[1] as RSetState<T>
return currentValue to setState
}

typealias RReducer<S, A> = (state: S, action: A) -> S
typealias RDispatch<A> = (action: A) -> Unit
fun <S, A> useReducer(reducer: RReducer<S, A>, initState: S, initialAction: A? = null): Pair<S, RDispatch<A>> {
val jsTuple = if (initialAction != null) {
rawUseReducer(reducer, initState, initialAction)
} else {
rawUseReducer(reducer, initState)
}
val currentState = jsTuple[0] as S
val dispatch = jsTuple[1] as RDispatch<A>
return currentState to dispatch
}

fun <S, A> useReducer(reducer: RReducer<S?, A>): Pair<S?, RDispatch<A>> {
return useReducer(reducer, null)
}

typealias RCleanup = () -> Unit
fun useEffectWithCleanup(dependencies: RDependenciesList? = null, effect: () -> RCleanup) {
if (dependencies != null) {
rawUseEffect(effect, dependencies.toTypedArray())
} else {
rawUseEffect(effect)
}
}

fun useEffect(dependencies: RDependenciesList? = null, effect: () -> Unit) {
val rawEffect = {
effect()
undefined
}
if (dependencies != null) {
rawUseEffect(rawEffect, dependencies.toTypedArray())
} else {
rawUseEffect(rawEffect)
}
}

fun useLayoutEffectWithCleanup(dependencies: RDependenciesList? = null, effect: () -> RCleanup) {
if (dependencies != null) {
rawUseLayoutEffect(effect, dependencies.toTypedArray())
} else {
rawUseLayoutEffect(effect)
}
}

fun useLayoutEffect(dependencies: RDependenciesList? = null, effect: () -> Unit) {
val rawEffect = {
effect()
undefined
}
if (dependencies != null) {
rawUseLayoutEffect(rawEffect, dependencies.toTypedArray()
)
} else {
rawUseLayoutEffect(rawEffect)
}
}