5 Reasons Static Sites Rock!

By James Quick

Static Sites are the new hotness in Web Development and rightfully so. Every day on podcasts, blog articles, and tweets I see and hear more and more people converting their sites. Let’s check out five reasons why so many people are moving to static sites!

https://youtu.be/ZWA9gx_jpWg

TLDR

Static Sites are incredibly powerful as well as fast, secure, and extremely easy to work with!

What Are Static Sites

Dynamic files are dynamically changed on the server and then served

Static files are static (prebuilt) and served without change from the server

Let’s start by explaining what static sites are. First, the term “static” does not mean that your site has no dynamic capabilities. It simply means that that your site consists of a set of “pre-built” files (namely html, js, and css files) that are directly served on request.

Dynamic sites, on the other hand, are being dynamically generated with each incoming request. Scotch.io frontend site is a mix between dynamic and static. The Scotch.io dashboard is a React SPA hosted completely on Netlify.

Because of this, you do not need a server (not one that you manage or you write code for) like Node or Apache. All your host provider needs is a way to return your static assets.

If you’re looking for a great host for your static site, check out Getting Started With Netlify!

1. Speed

The lack of database calls and dynamic content generation makes Static Sites incredibly fast!

Web Developers are constantly trying to improve speed, which in turn, improves user experience. Specifically, they put a lot of focus on Time to First Byte (TTFB), which measures the time it takes from the initial request to the first byte received by the browser.

Related Reading: Keep Server Response Times Low

Since content is pre generated, the time it takes to dynamically generate content is taken out of the picture! This means no database queries, no html templating, etc.

As mentioned, static sites don’t need a server that you have to manage. Since they are just static files, it’s easy for these files to be replicated across the world. This means that someone requesting your site from Japan can be served from a local data center insted of one on the other side of the world. Content Delivery Networks (CDNs) are built to do just this, which makes them a perfect host static sites. They are able to serve your site from as close as possible geographically to the person that requested it.

2. Simplicity in Hosting

Hosting for static sites can be setup in minutes!

Simplicity in hosting has a lot in common with the benefit of speed. As we just talked about, all your host provider has to do is serve static assets. The host doesn’t need to support a specific language or framework. In fact, it really doesn’t care anything about the specifics of your application. It just needs to know how to serve files, and serve them fast!

All of the top cloud providers have added specific plans for serving static sites, but a few of the newer hosts have really made a splash. Here are a few excellent hosts for your static sites.

Netlify

Netlify was one of the front-runners in coining and pushing the JAM Stack (JavaScript, APIs, and Markup) for modern static sites. Their interface is a breeze to use, the features they provide are incredible for working with static sites and static site generators. I’ve moved to Netlify for hosting of one of my sites and love it! Here are a few articles you can check out for more information.

Zeit

I hear more and more about Zeit and the amazing products they have. One that I’ve really enjoyed so far is Hyper, which is a terminal built in Electron. They also have support for static sites with Zeit Now hosting.

Amazon S3

Amazon’s cloud platform, AWS, was probably the first huge public cloud platform, used by individuals and enterprises alike. They have continued to add features to the platform over the years including file hosting with S3 which you can use for static sites.

Google Cloud Platform

Along with Amazon, the Google Cloud Platform is one of the biggest and most popular ones out there. They’ve got tons of different services. One of my favorites is Firebase, which is one of the easiest ways to spin up backend services for your app like a db, authentication, etc.

3. Security

No more having to update versions of your server… you’re always up to date!

One of the biggest reasons that servers get hacked is being behind on updates and patches. Often, patches for vulnerabilities are available, but people have yet to install them because it’s no easy feat to stay up to date with the constant stream of security fixes.

With static sites, however, because you don’t need to manage a server, you also don’t need to worry about updates, patches, etc. No more updating PHP. No more updating WordPress. No more updating Node. You’re always up to date!

This security concern is especially valid for different CMS’s, most notably WordPress. WordPress is incredibly popular (I’ve several WordPress sites myself), whhich, in turn, makes it heavily targeted. There are many obvious benefits to working with a CMS like WordPress, but having to stay up to date for security purposes is certainly a hassle!

4. Developer Experience

After setting up continuous deployment, just write code and push!

Developer Workflow/Experience is probably underrated when considering new technologies, frameworks, and ideas. With static sites, the experience is one of the most important benefits. After a bit of initial setup, the workflow is seamless. Here’es what the typical flow looks like.

As a developer of static sites you’ll typically follow these steps in your workflow.

  • Choose a host and create a site

  • connect your site to source control for Continuous Deployment

  • Make changes to your site and check them into source control

  • Continuous deployment grabs the latest code and triggers an automatic build of static assets

  • BOOM!! You’re site is updated and ready to go

Find out how to Deploy Your First Site to Netlify

In addition to the general workflow being straight forward, there are lots of Static Site Generators/Frameworks that make it even easier. These generators have tools built in to

For a list of Static Site Generators, you can check out Static Gen, but, here are a few of the more popular ones in the Web Development community.

5. Community Support

I’ve alluded to this a bit, but the community is throwing a lot of support behind static sites and static site generators. Netlify initially coined the term JAM Stack (JavaScript, APIs, and Markup) that has become a driving force behind modern static sites. The term and idea has caught on in a big way.

Netlify hosts a yearly conference called JAMstack Conf with some of the biggest name speakers in the industry.

With the plethora of static site generators out there, as mentioned above, there are so many different options for developers. There are options for various languages like JavaScript, Go, Ruby, and more. There are also options for different frameworks like Angular, React, and Vue as well. Lots of options and lots of support are a plus in my mind.

Things To Consider

With any new technology, framework, etc. there are always downsides, and static sites are no exception. So, as you go forward, keep these things in mind.

  • have to build site every time you add content

  • will not get dynamic content for each user

  • handling logins and forms in general become tricky

  • dependency on 3rd party services for “backend” functionality

Recap

The idea of static sites is not new (all sites were static originally), but what they have evolved into is. There are lots of options for static site generators and lots of great hosts out there. At the very least, I would recommend taking a look at one of your sites and see if it makes since to convert it to a static site. Obviously, there are some benefits to be gained.

I would love to know what your experience is with static sites. What hosts do you use? Which static site generators? Comment below or find me on twitter at @jamesqquick.

Source:: scotch.io

Get Started w/ Prisma for Super Simple GraphQL

By Chimezie Enyinnaya

Almost every week we get to see a new tool or product being released for GraphQL development. Yeah, that’s how hot the GraphQL ecosystem is currently is. Today, we’ll be looking at one of the tools, which is Prisma. We’ll be looking at how to get started with Prisma.

Prerequisites

To follow along, this tutorial assumes the following:

  • Node.js and NPM installed on your computer

  • Basic knowledge of GraphQL

What’s Prisma

Prisma is a data layer that turns a database into a GraphQL API.

We can look at it as a kind of ORM, but it’s much more powerful than traditional ORMs.

With Prisma, we get a server (Prisma server) that act as a proxy for our database and a high-performance query engine that runs on the server, which generates actual database queries for us. In addition to these, we also get a client (Prisma client), which we can use to interact with the server. Prisma also adds realtime event system to our database, so we can subscribe to database events in realtime.

Prisma works with both new and existing databases. In the case of an existing database, Prisma will generate the datamodel based on the database schema.

To learn about what Prisma is and why you should use it, do checkout the their official docs.

Overview of How it Works

Having seen what Prisma is, let’s take an overview look of how it works.

With a New Database

First, let’s look at how it works with a new database:

  • Create a new Prisma service
  • Set up and connect Prisma with your database
  • Define the datamodel
  • Deploy the Prisma API
  • Generate the Prisma client for your programming language of choice
  • Create a GraphQL server that uses the Prisma client to interact with your database

With an Existing Database

Now, let’s see how it works with an existing database:

  • Create a new Prisma service
  • Set up and connect Prisma with your existing database, using the Prisma Docker image
  • Generate the datamodel from your database schema
  • Deploy the Prisma API
  • Generate the Prisma client for your programming language of choice
  • Create a GraphQL server that uses the Prisma client to interact with your database

Note: To use Prisma locally, you need to have Docker installed on your machine. Note: Using an existing database with Prisma currently only works when using PostgreSQL databases.

Getting Started

To get started with Prisma, we need to install the Prisma CLI. There are a couple of ways to install the CLI, but we’ll be installing it using npm:

$ npm install -g prisma

With the CLI installed, we can start using it. We’ll use it to create a new Prisma service:

$ prisma init scotch-task-manager

Choose Demo server from the prompt to use an hosted demo server that already has database included, then we’ll be redirected to log in (or sign up if we don’t have an account yet) to Prisma Cloud and grant the CLI permissions to our account.

Note: Because we are using the demo server, which is hosted on Prisma cloud, hence the need of the Prisma cloud account. It is not required we have an account with Prisma cloud before we can use Prisma.

Back to the terminal, choose any of the provided region of the demo server. Then choose the defaults for both the name for our service and the name for our stage, that is, scotch-task-manager and dev respectively. Lastly, select Prisma JavaScript Client as the programming language for the generated Prisma client.

This will create two files: datamodel.prisma and prisma.yml inside the project directory. Also, a Prisma client will be auto-generated. We’ll go through these files shortly.

Based on the types defined inside datamodel.prisma, a GraphQL API is exposed with CRUD (Create, Read, Update and Delete) and realtime operations. This API is called the Prisma API.

Now, let’s deploy our newly created Prisma API:

$ cd scotch-task-manager
$ prisma deploy

This will deploy the default User type that Prisma created for us to our selected region. It will also display the live endpoints (HTTP and Websocket. We are only interested in HTTP in this tutorial) for our Prisma GraphQL database.

Now, we can take our new Prisma API for a spin in the GraphQL Playground:

$ prisma playground

Which we can access at http://localhost:3000/playground. So let’s create a new user with the following mutation:

mutation {
  createUser(data: { name: "Chimezie" }) {
    id
    name
  }
}

Understanding the Prisma files

Before we dive deep into building the GraphQL server for our app, let’s quickly go through the files Prisma generated for us.

  • prisma.yml: This is the configuration file for Prisma. It contains everything that’s required to set up a Prisma service.
  • datamodel.priama: Specifies the datamodel for our application that will be mapped to the database. In other words, it is used to define our database schema.
  • generated/prisma-client: Contains the auto-generated Prisma client library, which can be used to interact with the Prisma API.

Creating a GraphQL server

If all our GraphQL API will do is perform basic CRUD operations, then we would be fine with just the GraphQL API Prisma generated for us. In most cases we would want more than that, so we need to create a GraphQL server to handle our domain-specific logic as well as integrate with third-party services.

We’ll be using the graphql-yoga to create our GraphQL server. So let’s install it along with the other dependency we’ll be needing:

$ yarn add graphql-yoga prisma-client-lib

prisma-client-lib is needed to run the Prisma client that was generated for us.

Once that’s done installing, create a new directory called src directly within the project’s root directory.

$ mkdir src

Then let’s move the generated directory inside the src directory:

$ mv generated src

Next, within the src directory, create a new file called index.js:

$ cd src
$ touch index.js

Then add the following code to it:

// src/index.js

const { prisma } = require('./generated/prisma-client')
const { GraphQLServer } = require('graphql-yoga')
const resolvers = require('./resolvers')

const server = new GraphQLServer({
  typeDefs: './src/schema.graphql',
  resolvers,
  context: {
    prisma
  }
})

server.start(() => console.log('Server is running on http://localhost:4000'))

We import the Prisma client, graphql-yoga and our resolver functions (we’ll create in a moment). Then we create a new GraphQL server, passing to it our schema (we’ll create this in a moment), the resolver functions and lastly the context, which contains the Prisma client. We pass the Prisma client to the context so that we’ll be able to use it inside our resolver functions. Finally, we start the GraphQL server.

Updating the datamodel

Let’s define the datamodel of our app. The app will be made up of two types: Project and Task. So update datamodel.prisma as below:

# datamodel.prisma

type Project {
  id: ID! @unique
  name: String!
  tasks: [Task!]!
}

type Task {
  id: ID! @unique
  title: String!
  project: Project!
  isCompleted: Boolean! @default(value: "false")
}

The Project type has three fields; the ID of the project, the name of the project and lastly the project’s tasks. We use the @unique directive on the id field because we want it to be unique for each project and will be set as the primary key on the database table. All the fields are required (denoted with !). The tasks field will return a list of tasks that makes up the project because it is enclosed in []. Notice it has two !, which means the list of tasks can not contain null, but the whole list can be empty.

The Task type is similar to the Project type. It has a project field, which will be the particular project the task belongs to. Then a isCompleted field, which indicates whether a task has been completed or not. We give the field a default value of false using the @default directive.

Note: The default value must always be in double quotes, even for non-string types such as Boolean or Int.

With our datamodel, Project and Type have a one-to-many relationship. A project can have many tasks (because of the tasks field on the Project type) and a task belongs to a project (again, because of the project field on the Task type).

Tips: It is required to use the @relation directive to define a relation when a relation is ambiguous.

For the changes on our datamodel to take effect, we need to redeploy the Prisma API:

$ prisma deploy

Warning: If you get this warning You already have nodes for this model. This change will result in data loss. for the User type. This is because we created a user earlier and now we don’t need the User type anymore. So we just need to force the deploy: $ prisma deploy –force.

Also, we need to regenerate the Prisma client. We can do that by running the command below:

$ prisma generate

Defining the schema

Now let’s start defining the schema that would make up our GraphQL API. Within the src directory, create a new file called schema.graphql:

$ touch schema.graphql

Then add the following code in the newly created file:

# src/schema.graphql

type Query {
  projectById(projectId: ID!): Project
  completedTasks: [Task!]!
}

type Mutation {
  createProject(name: String!): Project
  createTask(title: String!, projectId: ID!): Task
  markTaskAsCompleted(taskId: ID!): Task
}

type Project {
  id: ID!
  name: String!
  tasks: [Task!]!
}

type Task {
  id: ID!
  title: String!
  project: Project!
  isCompleted: Boolean!
}

We define a queries to fetch a project by its ID and fetch tasks that have been marked as completed respectively. Also, we define the mutations to create a new project, a new task and to mark a task as completed. Finally, we define the schema for the types in our datamodel.

Tips: Redefining the schema for the types in the datamodel can be redundant and not easily maintainable most especially for large projects. To avoid redefining them, we can use tools like https://github.com/prisma/graphql-import.

Creating the resolver functions

With the schema defined, let’s create the resolver functions that would handle them. Inside the src directory, create a new file called resolvers.js and add the following code in it:

// src/resolvers.js

const resolvers = {
  Query: {
    projectById (root, { projectId }, { prisma }) {
      return prisma.project({ id: projectId })
    },
    completedTasks (root, args, { prisma }) {
      return prisma.tasks({ where: { isCompleted: true } })
    }
  },
  Mutation: {
    createProject (root, { name }, { prisma }) {
      return prisma.createProject({ name })
    },
    createTask (root, { title, projectId }, { prisma }) {
      return prisma.createTask({
        title,
        project: { connect: { id: projectId } }
      })
    },
    markTaskAsCompleted (root, { taskId }, { prisma }) {
      return prisma.updateTask({
        where: { id: taskId },
        data: { isCompleted: true }
      })
    }
  },
  Project: {
    tasks (root, args, { prisma }) {
      return prisma.project({ id: root.id }).tasks()
    }
  },
  Task: {
    project (root, args, { prisma }) {
      return prisma.task({ id: root.id }).project()
    }
  }
}

module.exports = resolvers

We start by creating the resolver functions for the queries. Remember we passed the Prisma client to the context, so we extract it from the context and use it to interact with the Prisma API. The projectById() simply returns the project whose ID matches the supplied project ID. The completedTasks() does pretty the same, but instead it returns a list of tasks that have been marked as completed (that is, isCompleted is true).

If we peep into the generated Prisma client, we’ll see that CRUD operations have been generated for us. So all we need to do is use them. The createProject() calls the createProject() on the Prisma client, which accepts the data we want to insert, in our case just the name of the project since the project’s ID will be auto-generated for us. In the same vein, the createTask() uses the createTask() on the Prisma client, but in addition to the data to be inserted, we connect the task to the project (through the supplied project ID) it belongs to. The markTaskAsCompleted() simply updates a task by setting isCompleted to true. It uses the updateTask(), which accepts two arguments: a where clause to get the task to be updated and the data to update it with.

Lastly, we to create the resolver functions to fetch the custom fields (tasks and project) on the types on our schema.

Tips: Use the singular form of a model to query for a single object and use the plural form of the model to query for a list of objects. E.g. You will notice we are using the singular form of Project to fetch a single project prisma.project({ id: projectId }) and the plural form of Task to fetch a list of tasks prisma.tasks({ where: { isCompleted: true } }).

Testing it out

Now we can test out our app. First, let’s start the GraphQL server:

$ node src/index.js

The server should be up and running at http://localhost:4000 and opening it will load up the GraphQL Playground.





Conclusion

In this tutorial, we looked at what Prisma is, then we looked at how to get started with it by building a GraphQL server. We also looked at how to test our GraphQL server using the GraphQL Playground. If you would like to learn more about Prisma, do check out the docs.

Source:: scotch.io

Build a React To-Do App with React Hooks (No Class Components)

By Sarah Jorgenson

Yes, a to-do app, seems like something we’ve all done in the past. The great thing about a to-do app is that it covers all 4 aspects of CRUD; Create, Read, Update, Delete. As a developer, we need to know CRUD and a to-do app is the best way to start that learning or refresh some of the things we may be a little rusty on.

React is the frontend JavaScript library we are going to be using today. Whether you are new to React or are an avid React user, we can all agree on one thing: React is pretty great.

We’ve already done a React To-Do App here at Scotch, but that was with Class components. Today we will have no Class components by integrating React Hooks, React’s newest upgrade feature. Let’s get started.

https://codesandbox.io/s/oj3qm2zq06

React Hooks

React is always improving and growing. The latest update is React 16.7, so new it’s in alpha stages. In this upgrade, we are given the power of React Hooks.

React Hooks allow for functional components to have a state and utilize lifecycle methods.

No longer do we need to rely on just class components for that functionality.

You can learn all about React Hooks here or visit React’s docs on Hooks here.

Starting a React App

Navigate to the place you would like your new application to be located and type:

npx create-react-app react-to-do

Note: Running npxbefore the command allows for it to be installed if it is not already installed globally on your machine.

Sit back and relax, React is taking care of the initial build for your new application.

Once it is done, you will need to navigate into the project:

cd react-to-do

and then to run the project:

npm run start

and navigate to http://localhost:3000/ to see the spinning React logo in your browser.

Styling Your Application

Jump into your src/App.css file and add in the three classes we will be using throughout our app. Styling won’t be the focus of this app, so we’ll keep this short and sweet.

src/App.css

.app {
  background: #209cee;
  padding: 30px;
  height: 100vh;
}

.todo-list {
  background: #e8e8e8;
  border-radius: 4px;
  padding: 5px;
  max-width: 400px;
}

.todo {
  background: #fff;
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.15);
  padding: 3px 10px;
  font-size: 12px;
  margin-bottom: 6px;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

Reading a List of Items. cRud.

With your application running and the styling ready to be used, let’s start on the Read part of CRUD. We’ll want to make a list of things, just so we can Read/view the list.

Adding in State

Go into your src/App.js file and let’s add a state to our component. We are going to be using React Hooks so state will look a little different than what you’re used to in classes.

src/App.js

function App() {
  const [todos, setTodos] = useState([
    { text: "Learn about React" },
    { text: "Meet friend for lunch" },
    { text: "Build really cool todo app" }
  ]);

  // we'll render our todos here ...
  // return <div></div>
}

The component, as we can see, is a functional component. In past versions of React, function components were unable to handle state, but now, by using Hooks, they can.

  • The first parameter, todos is what we are going to name our state.
  • The second parameter, setTodos is what we are going to use to set the state.

We’ve got a writeup on array destructuring if you want to know more info about that [todos, setTodos] syntax here.

The hook of useState is what React uses to “hook” into the state or lifecycle of the component. We then create an array of objects and we have the beginnings of our state.

Comparing to a Class Component

Let’s take a quick detour and see how this would’ve done with classes:

class App extends Component {
  state = {
    todos: [
      { text: "Learn about React" },
      { text: "Meet friend for lunch" },
      { text: "Build really cool todo app" }
    ]
  }

  setTodos = todos => this.setState({ todos });

  render() {
    return <div></div>
  }

}

A lot more typing. React Hooks let’s us make that really clean! We’ll continue with our functional component version from above.

Our “Todo” Component

We will want to create a component that we can use later on in the return of the main App component. We will call that Todo and it will pass in the (todo) and show the “text” part of the todo (todo.text), like so:

const Todo = ({ todo }) => <div className="todo">{todo.text}</div>;

Let’s see how we will use that in our App component.

Using Our Todo Variable to Get a List Returned

Go down to the return part of the App component and remove almost everything. We want to empty out that return part especially so that the spinning logo we saw earlier when we navigated to http://localhost:3000, goes away and we have our list being displayed on the page.

By using the JavaScript method, map(), we are able to create a new array of items by mapping over the todo items from state and displaying them by index.

Let’s create a pretty list of items:

src/App.js

  return (
    <div className="app">
      <div className="todo-list">
        {todos.map((todo, index) => (
          <Todo
            key={index}
            index={index}
            todo={todo}
          />
        ))}
      </div>
    </div>
  );

Navigate to your browser and you should see something like this:

Creating New Items to the To-Do List – Crud

Want to create a new item to the list? What if we forgot to add something to our to-do list and don’t want to forget that thing? Let’s give our application the power to Create a new item for our to-do app.

While in the src/App.js file, we are going to want to add a couple of things. At the top we are going to add another component, we’ll call it TodoForm. In this component we want it to:

  • Start with an empty state for an input field.
  • Be able to update the form by setting the state.
  • Handle the submit.

Setting our Empty State for the Form Input

Remember, we are using React Hooks so state is going to be a little different. To set our state we are going to want to write it like so:

const [value, setValue] = useState("");

The first is the “value” and the second is how we are going to be setting the state. The state starts off empty and as we add things to our state, it will add it to our list of to-do items.

We will want to add in a handleSubmit variable that can handle our addTodo function (we will make that function soon) and add the item to the list. If nothing is in the input box and the user presses “enter”, we want it to not do anything (i.e., not add in an empty tile to the list).

Adding that functionality into a form that has an input box, we should have our code look like this:

src/App.js

function TodoForm({ addTodo }) {
  const [value, setValue] = useState("");

  const handleSubmit = e => {
    e.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue("");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        className="input"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
    </form>
  );
}

The addTodo function I told you about earlier? Let’s go ahead and build that now. Staying within App.js, under the state of the App component, the function should be able to grab the existing list of items, add on the new item, and display that new list.

src/App.js

const addTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };

Notice the lack of this.state.? With the new React Hooks, we have no more need to use that. Can you use it? Sure, of course. But the new Hooks allow for less typing, more efficiency, and understand that this.state. is going to be implied in certain places.

See that spread operator? The three dots before the todos, that is essentially “copying” the list for us so that we are able to add on the new to-do item. Then using our keyword that we set earlier, we will set the state with setTodos.

By using the TodoForm down in the return of the App component, we will see that input box pop up now. The entire src/App.js file should look like this so far:

src/App.js

import React, { useState } from "react";
import "./App.css";

const Todo = ({ todo }) => <div className="todo">{todo.text}</div>;

function TodoForm({ addTodo }) {
  const [value, setValue] = useState("");

  const handleSubmit = e => {
    e.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue("");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        className="input"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
    </form>
  );
}

function App() {
  const [todos, setTodos] = useState([
    { text: "Learn about React" },
    { text: "Meet friend for lunch" },
    { text: "Build really cool todo app" }
  ]);

  const addTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };

  return (
    <div className="app">
      <div className="todo-list">
        {todos.map((todo, index) => (
          <Todo
            key={index}
            index={index}
            todo={todo}
          />
        ))}
        <TodoForm addTodo={addTodo} />
      </div>
    </div>
  );
}
  export default App;

Go to your browser and play around. You can now add in a to-do item to your list!

Updating Items in the To-Do List to be Completed – crUd

How would we want to update our to-do application? Maybe let’s have the functionality of being able to cross off an item. Let’s get started on that code!

Updating our State

Our state in our App component needs a little extra to it for the “Completed” status to be able to change. We will be adding in another key/value pair to our list of objects. By adding in an “isCompleted: false” value, we set that to false to begin with and will, when prompted, change that to true.

src/App.js

const [todos, setTodos] = useState([
     {
       text: "Learn about React",
       isCompleted: false
     },
     {
       text: "Meet friend for lunch",
       isCompleted: false
     },
     {
       text: "Build really cool todo app",
       isCompleted: false
     }
   ]);

We will need a function like the addTodo function but this one will be able to “complete” an item. We will want to do some similar things that we did in the addTodo like using the spread operator to grab the current list of items. In this function, we will be changing the isCompleted status to true so that it knows it has been completed. It will then update the state and set the state to the newTodos.

src/App.js

const completeTodo = index => {
     const newTodos = [...todos];
     newTodos[index].isCompleted = true;
     setTodos(newTodos);
   };

By using completeTodo in the Todo function, we are going to be able to fire off that functionality. When the “Complete” button is clicked, it will add in the textDecoration styling and cross-out the item. We are using a ternary operator, a feature within ES6 JavaScript, which is a simpler way of doing an if/else statement. This is our way of completing an item on the list and “updating” the list. The code should look as follows:

src/App.js

function Todo({ todo, index, completeTodo }) {
  return (
    <div
      className="todo"
      style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
    >
      {todo.text}

      <div>
        <button onClick={() => completeTodo(index)}>Complete</button>
      </div>
    </div>
  );
}

Dive down to the return of the App component and we’ll add in the following line:

completeTodo={completeTodo}

to look like this in the code:

src/App.js

<div className="card-content">
    {todos.map((todo, index) => (
        <Todo
            key={index}
            index={index}
            todo={todo}
            completeTodo={completeTodo}
        />
    ))}
</div>

Returning to the browser, your to-do app should look something like this when a “Complete” button is clicked.

Now we can read our list, add to our list, and update the completed status of each item. What’s left? Delete an item.

Deleting a To-Do Item – cruD

So you’ve completed an item on your to-do list, the day is over and you want to delete it from your list to be able to start over tomorrow. We are going to want to delete that item completely. Let’s see how we can get that going.

By adding just a couple lines, we will be able to add in the functionality of deleting an item.

We will go ahead and build the removeTodo function so that when we click on an “X” to delete an item, that will be fired off. That function will be located by the others underneath the state of the App component.

In this removeTodo function, we will again be using the spread operator but once we grab that current list, we will be “splicing” the chosen index off of the array of items. Once that is removed, we will return the new state by setting it with setTodos to be newTodos.

src/App.js

const removeTodo = index => {
     const newTodos = [...todos];
     newTodos.splice(index, 1);
     setTodos(newTodos);
   };

In your Todo function, you will want to add in this line:

<button onClick={() => removeTodo(index)}>x</button>

like this:

src/App.js

function Todo({ todo, index, completeTodo, removeTodo }) {
  return (
    <div
      className="todo"
      style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
    >
      {todo.text}

      <div>
        <button onClick={() => completeTodo(index)}>Complete</button>
        <button onClick={() => removeTodo(index)}>x</button>
      </div>
    </div>
  );
}

You’ll see as well that we are bringing in removeTodo at the top and then using it in the onClick of the “X”.

Adding in the removeTodo in the Todo part of the returning the App component, our “delete” will be fully functional. Add it in here:

src/App.js

<Todo
    key={index}
    index={index}
    todo={todo}
    completeTodo={completeTodo}
    removeTodo={removeTodo}
/>

With that added in, go to your browser and you’ll see a button with an “X” that when clicked, deletes the item completely.

The Final Product

The entire src/App.js file should look like this in the end:

src/App.js

import React, { useState } from "react";
import "./App.css";

function Todo({ todo, index, completeTodo, removeTodo }) {
  return (
    <div
      className="todo"
      style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
    >
      {todo.text}

      <div>
        <button onClick={() => completeTodo(index)}>Complete</button>
        <button onClick={() => removeTodo(index)}>x</button>
      </div>
    </div>
  );
}

function TodoForm({ addTodo }) {
  const [value, setValue] = useState("");

  const handleSubmit = e => {
    e.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue("");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        className="input"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
    </form>
  );
}

function App() {
  const [todos, setTodos] = useState([
    {
      text: "Learn about React",
      isCompleted: false
    },
    {
      text: "Meet friend for lunch",
      isCompleted: false
    },
    {
      text: "Build really cool todo app",
      isCompleted: false
    }
  ]);

  const addTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };

  const completeTodo = index => {
    const newTodos = [...todos];
    newTodos[index].isCompleted = true;
    setTodos(newTodos);
  };

  const removeTodo = index => {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };

  return (
    <div className="app">
      <div className="todo-list">
        {todos.map((todo, index) => (
          <Todo
            key={index}
            index={index}
            todo={todo}
            completeTodo={completeTodo}
            removeTodo={removeTodo}
          />
        ))}
        <TodoForm addTodo={addTodo} />
      </div>
    </div>
  );
}

export default App;

What have we learned?

A to-do app can be a great reminder or starting point when it comes to CRUD in web development. Being able to read information, create new information, update existing information, and deleting said information can be powerful in any application.

React Hooks are great. They allow for a more straight-forward way of coding and can make your code clear and concise.

Now go have fun adding all your many to-do items to your newly built to-do app. And then have even more fun crossing them off when you finish them!

Happy coding!

Source:: scotch.io