tribou / react-template

Build a server-rendered React app and share code across web, React Native, and Electron
Apache License 2.0
4 stars 9 forks source link

Add functional React examples using recompose and rambda (vs OOP classes) #61

Open tribou opened 6 years ago

tribou commented 6 years ago

This would let us move ALL the component handlers, lifecycle methods, and React state into the container. It might enable better sharing across RN/web with the exception of iOS/Android-specific modules that might need custom logic in the view still.

Before

https://github.com/tysoncadenhead/coffee-ratios/blob/e27766e4f22fe16608ae93a89499edd22bbd701d/src/App.tsx#L12

class App extends React.Component <any, any> {

    constructor (props: any) {
        super(props);

        this.setSelectedCoffee = this.setSelectedCoffee.bind(this);
        this.toggleIsFavorite = this.toggleIsFavorite.bind(this);

        this.state = {
            selectedCoffee: null,
            favorites: {},
            coffees: []
        };
    }

    setSelectedCoffee (selectedCoffee) {
        this.setState({
            selectedCoffee
        });
    }

    getFavorites () {
        fetchFavorites()
            .then(favorites => {
                this.setState({
                    favorites
                });
            });
    }

    getCoffees () {
        fetchCoffees()
            .then(coffees => {
                this.setState({
                    coffees
                });
            });
    }

    componentDidMount () {
        this.getCoffees();
        this.getFavorites();
    }

    toggleIsFavorite () {
        const id = this.state.selectedCoffee;
        const isFavorite = this.state.favorites[id];

        if (isFavorite) {
            return removeFromFavorites(id)
                .then(favorites => {
                    this.setState({
                        favorites
                    })
                });
        } else {
            return addToFavorites(id)
                .then(favorites => {
                    this.setState({
                        favorites
                    })
                });
        }
    }

    render() {
        return (
            <AppWrapper>
                <Thumbnails>
                    {this.state.coffees.map(coffee => (
                        <Thumbnail 
                            id={coffee.id} 
                            key={coffee.id} 
                            isFavorite={this.state.favorites[
                                coffee.id
                            ]}
                            selectedCoffee={this.state.selectedCoffee} 
                                onClick={() => {
                                this.setSelectedCoffee(coffee.id)
                            }} />
                    ))}
                </Thumbnails>
                <Coffee 
                    id={this.state.selectedCoffee} 
                    key={this.state.selectedCoffee}
                    isFavorite={this.state.favorites[
                        this.state.selectedCoffee
                    ]}
                    toggleIsFavorite={this.toggleIsFavorite}
                />
            </AppWrapper>
        );
    }
}

After

https://github.com/tysoncadenhead/coffee-ratios/blob/4fcd6af14ee2b19a52b1bdcc831d1efe2bf4a3d4/src/App.tsx#L16

const App = props => (
    <AppWrapper>
        <Thumbnails>
            {props.coffees.map(coffee => (
                <Thumbnail 
                    id={coffee.id} 
                    key={coffee.id} 
                    isFavorite={props.favorites[
                        coffee.id
                    ]}
                    selectedCoffee={props.selectedCoffee} 
                        onClick={() => {
                        props.setSelectedCoffee(coffee.id)
                    }} />
            ))}
        </Thumbnails>
        <Coffee 
            id={props.selectedCoffee} 
            key={props.selectedCoffee}
            isFavorite={props.favorites[
                props.selectedCoffee
            ]}
            toggleIsFavorite={props.toggleIsFavorite}
        />
    </AppWrapper>
);

const getFavorites = props =>
    fetchFavorites()
        .then(props.setFavorites);

const getCoffees = props =>
    fetchCoffees()
        .then(props.setCoffees);

const toggleIsFavorite = R.curry((props, _e) => {
    const id = props.selectedCoffee;
    const isFavorite = props.favorites[id];
    const request = R.ifElse(
        R.equals(true),
        R.always(removeFromFavorites),
        R.always(addToFavorites)
    )(isFavorite);

    return request(id).then(props.setFavorites);
});

export default compose(
    withState('selectedCoffee', 'setSelectedCoffee', null),
    withState('favorites', 'setFavorites', {}),
    withState('coffees', 'setCoffees', []),
    componentDidMount(
        compose(
            R.tap(getCoffees),
            R.tap(getFavorites)    
        )
    ),
    withHandlers({
        toggleIsFavorite
    })
)(App);