Hyperapp-like native view demo

based on code generated by react-native init

by Christopher J. Brody aka @brodybits (Chris Brody)

LICENSE: ISC OR MIT

About

Simple counter app demo with state/action management system based on Hyperapp API, using elements supported by react-primitives, working on React Native and react-native-web. Inspired by Hyperapp demo in https://github.com/hyperapp/hyperapp#getting-started.

MOTIVATION:

See also: https://github.com/brodybits/hyperapp-rewrite-demo-on-inferno-and-superfine - demo rewrite of Hyperapp state/action management working with functional (stateless) components on multiple VDOM APIs: Inferno (React API) and Superfine

NOTE: This project no longer conforms to the Hyperapp API for specifying actions and views. Version with partial conformance to Hyperapp 1.x API is available in the strict-api-1 branch.

Build and run

First step: npm install

Android: react-native run-android

iOS: react-native run-ios or open ios/HyperappLikeNativeViewDemo.xcodeproj and run from Xcode

Run on codesandbox.io

(using react-native-web)

Paste the contents of App.js into App.js in https://codesandbox.io/s/q4qymyp2l6

(see react-native-web#quick-start)

Using react-primitives

see below

Quick tour

Top-level

Top-level app functions with initial state, actions, effects (side effects such as I/O, timers, I/O, other asynchronous operations, and other non-pure functions), and view in JSX (partially inspired by Hyperapp demo app in https://github.com/hyperapp/hyperapp#getting-started):

const App = () => (
  <ManagedAppView
    state={{ count: 0 }}
    actions={{
      up: (state) => ({ count: state.count + 1 }),
      dn: (state) => ({ count: state.count - 1 }),
    }}
    effects={{
      up3: (actions, effects) => {
        actions.up()
        setTimeout(effects.up2, 400)
      },
      up2: (actions, effects) => {
        actions.up()
        setTimeout(actions.up, 400)
      },
      dn3: (actions, effects) => {
        actions.dn()
        setTimeout(effects.dn2, 400)
      },
      dn2: (actions, effects) => {
        actions.dn()
        setTimeout(actions.dn, 400)
      },
    }}>
    <MyAppView />
  </ManagedAppView>
)

const MyAppView = ({ state, actions, effects }) => (
  <View style={styles.container}>
    <Text style={styles.welcome}>
      Hyperapp micro rewrite demo on React Native
    </Text>
    <Text>···</Text>
    <MyTouchButton
      style={styles.mybutton}
      onPress={effects.up3}
      title="Up 3"
    />
    <Text>···</Text>
    <MyTouchButton
      style={styles.mybutton}
      onPress={actions.up}
      title="Up (+1)"
    />
    <Text style={styles.welcome}>
      {state.count}
    </Text>
    <MyTouchButton
      style={styles.mybutton}
      onPress={actions.dn}
      title="Down (-1)"
    />
    <Text>···</Text>
    <MyTouchButton
      style={styles.mybutton}
      onPress={effects.dn3}
      title="Down 3"
    />
  </View>
)

export default App

Generic ManagedAppView component

Generic ManagedAppView component that supports the Hyperapp action/state/view API:

const ManagedAppView = createReactClass({
  getInitialState () {
    const ac = {}
    const ef = {}
    const self = this
    for (let a in this.props.actions) {
      ac[a] = () => {
        self.setState(prev => ({ ac: ac, st: (this.props.actions[a](prev.st)) }))
      }
    }
    for (let e in this.props.effects) {
      ef[e] = () => {
        this.props.effects[e](ac, ef)
      }
    }
    return { ac: ac, st: this.props.state, ef: ef }
  },
  render () {
    return React.Children.map(this.props.children, ch => (
      React.cloneElement(ch, { state: this.state.st, actions: this.state.ac, effects: this.state.ef })
    ))
  }
})

MyTouchButton component

Functional MyTouchButton component using TouchableWithoutFeedback imported as Touchable for portability to react-primitives:

const MyTouchButton = (props) => {
  const { onPress, title, ...other } = props

  return (
    <View {...other}>
      <Touchable onPress={onPress}>
        <Text>{title}</Text>
      </Touchable>
    </View>
  )
}

Demo styles

Demo styles with help from react-native init and guidance in https://facebook.github.io/react-native/docs/touchablehighlight:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  mybutton: {
    alignItems: 'center',
    backgroundColor: '#DDDDDD',
    padding: 10
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
})

Use with react-primitives

App.js changes for react-primitives

--- a/App.js
+++ b/App.js
@@ -17,9 +17,9 @@ import createReactClass from 'create-react-class'
 import {
   StyleSheet,
   Text,
-  TouchableWithoutFeedback as Touchable,
+  Touchable,
   View
-} from 'react-native'
+} from 'react-primitives'
 
 const App = () => (
   <ManagedAppView

react-primitives on codesandbox.io

react-primitives on mobile app

  • npm install react-primitives
  • Apply the changes above to App.js

TODO

Urgent

TBD

Near-term

  • Integrate build and run on browser using react-native-web (#1)
  • Nice touch button onPress animation

Future