reconceptualize your entire app as a function

github: @adamterlson

twitter: @adamterlson

adam.terlson@gmail.com

are you leveraging react's functional paradigms?

Minnesota, USA

Munich, Germany

TODO:
establish a personal connection with the audience

JSX is the best template language
out there!

JSX is the best template language 
javascript extension out there!

your app’s UI, features, routing, everything is the product of invoking functions with some arguments

your app isn’t what invokes the functions, your app is what gets returned

the 4.0 minute project:

Display the user's profile image

When the button is pressed, send the message.

Draft the message, when <Enter> is pressed, send.

abstracting 3rd party dependencies

  • code reuse
  • hide complexity
  • add functionality
  • standardized/enforced usage

react-provided components are a 3rd party dependency

create your own  functional "HTML":

  • suite of app-agnostic, semantic components

  • like making your own version of Bootstrap

HTML doesn't know about the app that you're building or your company

don't use low-level HTML or React Native components everywhere

WARNING:
This presentation may trigger seizures for developers with sensitivity to rapidly flashing code. 

Viewer discretion is advised.

step 1:

presentational components

import React from 'react'
import { 
    TextInput as RNTextInput 
} from 'react-native'

const TextInput = ({ value, onChange }) => (
    <RNTextInput
        value={value}
        onChange={onChange}
    />
)

export default TextInput
import React from 'react'
import { 
    TextInput as RNTextInput 
} from 'react-native'

const TextInput = ({ placeholder, value, onChange }) => (
    <RNTextInput
        placeholder={placeholder}
        value={value}
        onChange={onChange}
        style={{
            borderWidth: 1,
            borderColor: 'black',
        }}
    />
)

export default TextInput
<TextInput>

=

+

+

step 2:

add functionality via HOC's

HOC: function that takes a react component and returns a new component

HOC's are composable

separation between functionality and presentation

perform prop manipulation

e.g. react-redux's
connect()

import React from 'react'
import { 
    TextInput as RNTextInput 
} from 'react-native'

const TextInput = ({ placeholder, value, onChange }) => (
    <RNTextInput
        placeholder={placeholder}
        value={value}
        onChange={onChange}
        style={{
            borderWidth: 1,
            borderColor: 'black',
        }}
    />
)

export default TextInput
<TextInput>
import React from 'react';

export default WrappedComponent => {
    const HOC = props => (
        <WrappedComponent {...props} />
    );

    return HOC;
}
import React from 'react';
import store from '../store'; // redux api

const noop = x => x

export default (
    stateProps = noop, 
    dispatchProps = noop
) => WrappedComponent => props => (
    <WrappedComponent 
        {...props} 
        {...stateProps(store.getState())}
        {...dispatchProps(store.dispatch)}
    />
);
connect()
import React from 'react';
import store from '../store'; // redux api

const noop = x => x

export default (
    stateProps = noop, 
    dispatchProps = noop
) => WrappedComponent => {
    class ConnectHOC extends React.PureComponent {
        constructor(props) {
            super(props);
            this.state = store.getState();
        }
        componentDidMount() {
            this.unsubscribe = store.subscribe(
                () => this.setState(store.getState())
            );
        }
        componentWillUnmount() {
            this.unsubscribe();
        }
        render() {
            return (
                <WrappedComponent 
                    {...props} 
                    {...stateProps(this.state)}
                    {...dispatchProps(store.dispatch)}
                />
            );
        }
    }

    return ConnectHOC;
);
import React from 'react'

export default register => Component => props => {
    const listeners = []
    const listenTo = (key, handler) =>
        listeners.push(e => {
            if (e.key === key) {
                handler(e.target.value)
            }
        })

    // Note that props are passed back
    register(listenTo, props)

    return (
        <Component
            onKeyDown={e => listeners.forEach(cb => cb(e))}
            {...props}
        />
    )
}
keyboardEvents()
import TextInput from './TextInput'
import connect from './connect'

export default connect(
    null,
    dispatch => ({ 
        onSend: (message) => dispatch({ 
            type: 'SEND',
            payload: message,
        })
    })
)(TextInput)
import TextInput from './TextInput'
import connect from './connect'
import keyboardEvents from './keyboardEvents'

export default connect(
    null,
    dispatch => ({ 
        onSend: (message) => dispatch({ 
            type: 'SEND',
            payload: message,
        })
    })
)(keyboardEvents(
    (listenTo, { onSave }) =>
        listenTo('Enter', onSend)
)(TextInput))
import TextInput from './TextInput'
import connect from './connect'
import keyboardEvents from './keyboardEvents'
import { flowRight as compose } from 'lodash'

export default compose(  // <--- :D
    connect(
        null,
        dispatch => ({ 
            onSend: message => dispatch({ 
                type: 'SEND',
                payload: message,
            })
        })
    )
    keyboardEvents(
        (listenTo, { onSend }) =>
            listenTo('Enter', onSend)
    )
)(TextInput)
<TextInputSendMessage>>

Step 3:
Build with legos!

=

+

// Takes no props!
const SendMessage = () => (
    <Container>
        <CircularImageUser size={150} />
        <TextInputMessage 
            placeholder="Message..." 
        />
        <ButtonSend>
            <Icon type="mail" />
            Send Message!
        </ButtonSend>
    </Container>
);

SendMessage

<SendMessage>

summary

UI is made by layered functional abstraction, can be described with exclusively pure functions

higher order components can be used to build "vertically", to add behavior to UI

what do we get?

reusable, granular presentation separate from functionality

units of functionality that can be layered onto presentational components

clarity at the top level of the application: see how the independent parts are composed

ONE MORE THING

Children As Function

Hoist data to the layout for composition

import React from 'react';

export default (props) => (
    props.children(props)
);

ChildrenAsFunction

import connect from './connect'
import ChildrenAsFunction from './ChildrenAsFunction'

export default connect(
    ({ currentUser }) => ({
        name: currentUser.name,
        photoUrl: currentUser.userPhotoURL,
    })
)(ChildrenAsFunction)

CAFUser

const SendMessage = () => (
    <CAFUser>
        {({ name, photoUrl }) => (
            <Container>
                <CircularImage // Delete CircularImageUser
                    source={photoUrl} 
                    size={150} 
                />
                <TextInputMessage 
                    placeholder={`Message ${name}...`}
                />
                <ButtonSend>
                    <Icon type="mail" />
                    Send Message to {name}!
                </ButtonSend>
            </Container>
        )}
    </CAFUser>
);

twitter: @adamterlson

github: @adamterlson

adam.terlson@gmail.com

feedback welcome!

For comparison:
github.com/tastejs/todomvc

Thinking functionally in react

By Adam Terlson

Thinking functionally in react

A demonstration of the power of thinking functionally in react by way of example

  • 1,193