August 2

React Native: ToDo App

So the time as finally come… To make a React Native app! This app will just be a “port” of my previous ToDo app, which means it will take advantage of the database (MySQL) and API (Node.js + Express) that I made previously. Let’s begin! Note: I’ve made some big refactor changes since the React.js, but the app’s logic still functions the same.

While React.js is considered a JavaScript library for making websites, React Native is seen as a framework for building native apps for both Android and iOS. React Native works a lot like React.js, but it has some differences. First order of business was to learn more about those differences. I scouted on YouTube to find some tutorial videos to watch. I found The Net Ninja React Native tutorial series and binged watched a lot of them. After a while I felt confident with making my ToDo app.

Two of the biggest differences between React and React Native is that React Native doesn’t use CSS, but something called styles and “StyleSheets” to mimic CSS. The other one is that React Native uses different tags than React. Example, instead of “div”, you have “View”. You also just can’t type out text to be rendered. You have to use the “Text” tag.

Now let’s really begin! This is how the final app looks like:

Since the app has a lot of similarities to the React app, I won’t be covering the parts that are the same. Just know that we have functions to communicate with an API. All of the code can be viewed through the GitHub page.

The only thing different in the App.js file is how the render function and a StyleSheet object in the bottom of the file, instead of a CSS file:


...

  render() {
    return (
      // Body container
      <View style={styles.body} >
        <StatusBar style="auto" />
        <Header />

        {/* Main  */}
        <ScrollView style={styles.wrapper} contentContainerStyle={{ paddingBottom: 80 }} showsVerticalScrollIndicator={false} >

          {/* Shows all todos from state.todos */}
          {this.state.todos.map(toDo => {
            return <ToDo key={toDo.id} id={toDo.id} desc={toDo.desc} completed={toDo.completed} setAsCompleted={this.setAsCompleted} remove={this.removeToDo} />
          })}

        </ScrollView>

        {/* Menu and button that allows user to add new ToDos */}
        <AddToDo addToDo={this.addToDo} />

        <Footer />

      </View>
    );
  }
}

const styles = StyleSheet.create({

  body: {
    flex: 1,
    backgroundColor: '#ccc',

  },

  wrapper: {
    width: '95%',
    height: 100,

    marginLeft: 'auto',
    marginRight: 'auto',

    marginTop: 'auto',
    marginBottom: 5,

  },
});

Here you can see that I use “View” tags instead of “div”. The main difference is that in React Native we can use a ScrollView for our list of ToDos. Every object we place in React Native is set in a kind off “fixed position”, but with ScrollView we can have a scrollable space, like this:

The styling syntax is also really similar to CSS. The main difference is that here we use “camelCase” notation instead of using hyphens. Styles also have a fewer properties and values to use, but they usually do the job very well. It’s also worth nothing that we can only choose “flex” or “none” values in the “display” property, unlike CSS which has many values to choose between. However, flex is extremely strong and… well, flexible.

Since we don’t have access to CSS we have to do animations a different way too. Here’s how I did it for my “widen animation” for the ToDo component:

First I create the animation object in my state and also an interpolate var. This transform the value from 0-1 to 0%-100%, so I can use it for the CSS styling.


    state = {
        widenAnim: new Animated.Value(0),
    };

    interpolateWidth = this.state.widenAnim.interpolate({inputRange:[0,1],outputRange:['0%','100%']});

Next I create a function for starting the animation and call it when the component mounts:


    widen = () => {
        Animated.spring(this.state.widenAnim, {
            toValue: 1,
            useNativeDriver: false
        }).start();
    };

    componentDidMount() {
        this.widen();
    }

The spring function creates a spring-like animation. It has a lot of different properties we can change, but that wasn’t needed for what I wanted.

Last piece of the puzzle is to connect our animation to our render function. To make the entire component be effected by the animation I have to wrap it all up in a “Animated.View” tag, and make a style which is connected to the animation values. This is how the render function ended up like:


render() {

        // Destructure props
        const { id, desc, completed } = this.props;

        // Decides if the text should be shown with a lined-through effect
        let descStyle = completed ? [styles.component_todo_desc, styles.lined_through] : styles.component_todo_desc;

        return (
            <Animated.View style={{ opacity: this.state.fadeInAnim, width: this.interpolateWidth }}>
                <TouchableOpacity style={styles.component_todo} onPressIn={() => this.props.setAsCompleted(id)}>
                    {/* Main component ToDo div */}
                    <View style={{ flexDirection: "row", }}>
                        {/* Checkmark */}
                        <View style={styles.component_todo_checkmark_box} />
                        {completed ? <Text style={styles.component_todo_checkmark_check}>&#10003;</Text> : <Text />}

                        {/* Show desc text */}
                        <Text style={descStyle}>{desc}</Text>
                    </View>

                    {/* Delete button */}
                    <TouchableOpacity style={styles.component_todo_delete_button} onPressIn={() => this.props.remove(id)}>
                        <Entypo name="cross" size={48} color='rgb(90, 0, 0)' style={{textAlign:'center'}} />
                    </TouchableOpacity>

                </TouchableOpacity>
            </Animated.View>
        )
    }

Also, to create a touchable component we need to use the tag “TouchableOpacity” instead of Button.

I also made a fade in animation to go along with the widen animation. This is how it ended up like:

And that’s it! A port of the React ToDo app over to React Native, using MySQL as a database and Node.js + Express as an API. My goal for the summer was to create a full stack React Native application, and even though I’m happy about the app there’s still a lot more I need to go through (like React Navigation, etc…), but that will have to be for another time!

Currently I’m looking at trying to create an API using PHP and the Laravel framework instead, so stay tuned for that!



Copyright 2020. All rights reserved.

Posted August 2, 2020 by Atle in category "Blog

Leave a Reply

Your email address will not be published. Required fields are marked *