Categories
React

Getting Started with Creating International React Apps

Many apps have to be made usable by different users from various parts of the world.

To make this easier, we can use the react-intl to do the internationalization for us.

In this article, we’ll look at how to get started with the react-intl library.

Getting Started

To get started, we install the package by running:

npm install react-intl --save

Then we can use the IntlProvider to our app to let us use it:

import React from "react";
import { IntlProvider, FormattedMessage } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  const [name] = React.useState("james");
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedMessage id="greeting" values={{ name }} />
      </p>
    </IntlProvider>
  );
}

We have the translated messages in the messages object.

The keys are the locale.

Messages are taken from the messages object and passed into the messages prop.

Then we used the FormattedMessage component to display the message as specified by the ID.

values has the values for the placeholder.

name is the placeholder in the message, so that’s what we passed in.

Formatting Time

We can format time using the FormattedDate component.

For instance, we can write:

import React from "react";
import { IntlProvider, FormattedDate } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedDate
          value={new Date()}
          year="numeric"
          month="long"
          day="numeric"
          weekday="long"
        />
      </p>
    </IntlProvider>
  );
}

Then we get the formatted date formatted for any locale.

year sets the year format.

month sets the month format.

day sets the day format.

weekday sets the weekday format.

numeric means it’s displayed as a number.

long is displayed as the full word.

FormattedDateParts

There’s also the FormattedDateParts component to divide a date into their components.

This lets us have more customization in how to display the date.

For example, we can write:

import React from "react";
import { IntlProvider, FormattedDateParts } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedDateParts
          value={new Date()}
          year="numeric"
          month="long"
          day="2-digit"
        >
          {parts => (
            <>
              <b>{parts[0].value}</b>
              {parts[1].value}
              <em>{parts[2].value}</em>
            </>
          )}
        </FormattedDateParts>
      </p>
    </IntlProvider>
  );
}

to format our date parts with different formatting.

Then the first entry of parts is the month.

The 2nd is the comma.

And the 3rd is the day.

FormattedTime

To just show the internationalized time, we can use the FormattedTime component.

For example, we can write:

import React from "react";
import { IntlProvider, FormattedTime } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedTime value={new Date(1459832991883)} />
      </p>
    </IntlProvider>
  );
}

to show a formatted time.

FormattedTimeParts

Like with dates, we can divide the time into parts and format that.

We can use the FormattedTimeParts component to do that.

For example, we can write:

import React from "react";
import { IntlProvider, FormattedTimeParts } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedTimeParts value={new Date()}>
          {parts => (
            <>
              <b>{parts[0].value}</b>
              {parts[1].value}
              <em>{parts[2].value}</em>
            </>
          )}
        </FormattedTimeParts>
      </p>
    </IntlProvider>
  );
}

The parts array has the hour as the first entry.

The 2nd is the colon and the 3rd is the minute.

Conclusion

We can add translations easily with the react-intl package.

Also, we can format the time the way we like.

Categories
Vue 3

Vue 3 — Component Events

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to listen to component events.

Listening to Child Components Events

We can listen to child components within the parent component.

Our child component has to emit the event so that the parent component can listen to it.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <div :style="{ 'font-size': `${fontSize}px` }">
        <todo-item
          @enlarge-text="fontSize++"
          v-for="t of todos"
          :todo="t"
          :key="t.id"
        ></todo-item>
      </div>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            fontSize: 15,
            todos: [
              { id: 1, name: "eat" },
              { id: 2, name: "drink" },
              { id: 3, name: "sleep" }
            ]
          };
        }
      }); 
      app.component("todo-item", {
        props: ["todo"],
        template: `
          <div>
            <p>{{ todo.name }}</p>
            <button @click="$emit('enlarge-text')">
              Enlarge text
            </button>
        </div>
        `
      }); 
      app.mount("#app");
    </script>
  </body>
</html>

We created a todo-item component with an ‘enlarge text’ button.

When we click it, the $emit function is run.

The argument is the event name.

Then in the parent component that holds the todo-item component, we have the @enlarge-text directive so that we can listen to the enlarge-text event that’s emitted from todo-item .

When that event is received, when we increase fontSize by 1.

Since we set fontSize as the font size of our div, then font size change will be applied across all the child elements.

Emitting a Value With an Event

The $emit function takes a second argument with the value we want to emit with the event.

Therefore, we can modify our code to pass the value into the $emit function.

Then we get the value emitted from the $event object.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <div :style="{ 'font-size': `${fontSize}px` }">
        <todo-item
          @enlarge-text="fontSize += $event"
          v-for="t of todos"
          :todo="t"
          :key="t.id"
        ></todo-item>
      </div>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            fontSize: 15,
            todos: [
              { id: 1, name: "eat" },
              { id: 2, name: "drink" },
              { id: 3, name: "sleep" }
            ]
          };
        }
      }); 

      app.component("todo-item", {
        props: ["todo"],
        template: `
          <div>
            <p>{{ todo.name }}</p>
            <button @click="$emit('enlarge-text', 1)">
              Enlarge text
            </button>
          </div>
        `
      }); 
      app.mount("#app");
    </script>
  </body>
</html>

$emit takes a second argument that we emit with the enlarge-text event.

Now in the parent component, we get the value emitted with the $event variable.

In this case, $event is set to 1 since that’s what we emitted.

We changed the @enlarge-text ‘s value so that the fontSize updates from the $event instead of a constant value.

Also, we can put the expression we passed into @enlarge-text into a method.

This is handy if we have more code.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="[https://unpkg.com/vue@next](https://unpkg.com/vue@next)"></script>
  </head>
  <body>
    <div id="app">
      <div :style="{ 'font-size': `${fontSize}px` }">
        <todo-item
          @enlarge-text="onEnlargeText"
          v-for="t of todos"
          :todo="t"
          :key="t.id"
        ></todo-item>
      </div>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            fontSize: 15,
            todos: [
              { id: 1, name: "eat" },
              { id: 2, name: "drink" },
              { id: 3, name: "sleep" }
            ]
          };
        },
        methods: {
          onEnlargeText(amount) {
            this.fontSize += amount;
          }
        }
      }); 

      app.component("todo-item", {
        props: ["todo"],
        template: `
          <div>
            <p>{{ todo.name }}</p>
            <button @click="$emit('enlarge-text', 1)">
              Enlarge text
            </button>
        </div>
        `
      }); 
      app.mount("#app");
    </script>
  </body>
</html>

We changed @enlarge-text ‘s value to our onEnlargeText method.

amount would be automatically set to the $event variable’s value, which is 1.

So we get the same result.

Conclusion

We can listen to the child component’s events from the parent.

This way, we can pass data from child to parent.

Categories
Vue 3

Vue 3 — Component Basics

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create simple Vue 3 components.

Components

Components are reusable Vue instances with a name.

For instance, we can create a component by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <button-counter ></button-counter>
    </div>
    <script>
      const app = Vue.createApp({}); 
      app.component("button-counter", {
        data() {
          return {
            count: 0
          };
        },
        template: `
          <div>
            <button @click="count--">
              decrement
            </button>
            <p>{{ count }}</p>
          </div>
        `
      }); 
      app.mount("#app");
    </script>
  </body>
</html>

We create a component with a button and a p element.

When we click the button, the count is decrement, and the value is displayed in the p element.

Then we used the button-counter element in the app’s template.

We can reuse the component multiple times.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <button-counter></button-counter>
      <button-counter></button-counter>
      <button-counter></button-counter>
    </div>
    <script>
      const app = Vue.createApp({}); 
      app.component("button-counter", {
        data() {
          return {
            count: 0
          };
        },
        template: `
          <div>
            <button @click="count--">
              decrement
            </button>
            <p>{{ count }}</p>
          </div>
        `
      }); 
      app.mount("#app");
    </script>
  </body>
</html>

Now we have 3 instances of the button-counter component.

They all have their own template and state.

Organizing Components

We got to organize our components well since we’ll probably have many of them in an app.

We can put them in a tree.

So we divide our app into small components we nest components to build what we want.

Passing Data to Child Components with Props

Our previous example didn’t have any way to get data from a parent component.

However, Vue 3 provides us with a way to pass data from parent to child with props.

Props are custom attributes that we can register on a component.

When a value is passed into the prop attribute, it becomes a property of the component instance.

For instance, we can create a component that takes props and use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <blog-post title="hello world"></blog-post>
    </div>
    <script>
      const app = Vue.createApp({}); 
      app.component("blog-post", {
        props: ["title"],
        template: `<h1>{{ title }}</h1>`
      }); 
      app.mount("#app");
    </script>
  </body>
</html>

We create the blog-post component with the title prop so that we can display it on the template.

The props property has an array of strings of the prop names.

Having that means we registered that props to be allowed to be passed into our component.

A component can have as many props as we want.

If we have an array of data, we can use the v-for directive to render them.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <todo-item v-for="t of todos" :todo="t" :key="t.id"></todo-item>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            todos: [
              { id: 1, name: "eat" },
              { id: 2, name: "drink" },
              { id: 3, name: "sleep" }
            ]
          };
        }
      }); 

      app.component("todo-item", {
        props: ["todo"],
        template: `<p>{{ todo.name }}</p>`
      }); 
      app.mount("#app");
    </script>
  </body>
</html>

to render an array with components.

We have the todo-item component with the todo prop registered.

The template property rendered the name property of the array.

todo-item is used with v-for to render the todo items.

We pass in the todo value with the todo prop with :todo='t' and do the same with the key prop.

The key prop is used to let Vue identify each element uniquely.

Conclusion

We can create our own components with the app.component method so that we can divide our Vue app into manageable pieces.

Categories
Gatsby.js

Gatsby.js — Display a Single Image

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Get and Display a Single Image

We can get and display a single image with Gatsby.

We use the gatsby-image to get and add an image.

To install the packages required packages, we run:

npm i gatsby-plugin-sharp gatsby-transformer-sharp

To do this, we write:

gatsby-config.js

const path = require('path');

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: path.join(__dirname, `src`, `images`),
      },
    },
    `gatsby-plugin-sharp`,
    `gatsby-transformer-sharp`,
  ],
}

Then we can get the image on our page by writing:

src/pages/index.js

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const IndexPage = () => {
  const data = useStaticQuery(graphql`
    query {
      file(relativePath: { eq: "laptop.jpg" }) {
        childImageSharp {
          fluid {
            base64
            aspectRatio
            src
            srcSet
            sizes
          }
        }
      }
    }
  `)

  return (
    <div>
      <Img fluid={data.file.childImageSharp.fluid} alt="laptop" />
    </div>
  )
}
export default IndexPage

We specify that we want to get laptop.jpg in our GraphQL query.

Then we get the fields and put them into the Img component in the JSX.

We can simplify this by writing:

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const IndexPage = () => {
  const data = useStaticQuery(graphql`
    query {
      file(relativePath: { eq: "laptop.jpg" }) {
        childImageSharp {
          fluid(maxWidth: 200, quality: 75) {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `)

  return (
    <div>
      <Img
        fluid={data.file.childImageSharp.fluid}
        alt="laptop"
        style={{ border: "2px solid purple", borderRadius: 5, height: 250 }}
      />
    </div>
  )
}
export default IndexPage

We simplify the query with the GatsbyImageSharpField fragment, which is equivalent to what we have in the previous example.

And we add the style into our Img component.

We can set the fluid prop to force an aspect ratio by overriding the aspectRatio field:

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const IndexPage = () => {
  const data = useStaticQuery(graphql`
    query {
      file(relativePath: { eq: "laptop.jpg" }) {
        childImageSharp {
          fluid(maxWidth: 200, quality: 75) {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `)

  return (
    <div>
      <Img
        fluid={data.file.childImageSharp.fluid}
        alt="laptop"
        fluid={{
          ...data.file.childImageSharp.fluid,
          aspectRatio: 1.6,
        }}
      />
    </div>
  )
}
export default IndexPage

Also, we can set the image with a fixed-width with the following query:

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const IndexPage = () => {
  const data = useStaticQuery(graphql`
    query {
      file(relativePath: { eq: "laptop.jpg" }) {
        childImageSharp {
          fixed(width: 250) {
            ...GatsbyImageSharpFixed
          }
        }
      }
    }
  `)

  return (
    <div>
      <Img fixed={data.file.childImageSharp.fixed} alt="laptop" />
    </div>
  )
}
export default IndexPage

We have the fixed field with the width set to 250.

So the image will be displayed with 250px width.

Conclusion

We can display a single image in various ways by using various queries with Gatsby.

Categories
Gatsby.js

Gatsby.js — Filtering and Images

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Filtering with GraphQL

We can filter items in our results with our GraphQL queries.

For example, we can write:

{
  allSitePage(filter: {path: {eq: "/pokemon"}}) {
    edges {
      node {
        id
        path
      }
    }
  }
}

Then we get the path that equals to to /pokemon .

eq means equals.

GraphQL Query Aliases

We can add GraphQL query aliases.

For example, we can write:

{
  fileCount: allFile {
    totalCount
  }
  filePageInfo: allFile {
    pageInfo {
      currentPage
    }
  }
}

fileCount and filePageInfo are the aliases.

And the expression after the colon are the queries.

GraphQL Query Fragments

GraphQL query fragments are shareable chunks of a query that can be reused.

For example, we can create a fragment and make a query with it by writing:

src/pages/index.js

import React from "react"
import { graphql } from "gatsby"

export const query = graphql`
  fragment SiteInformation on SiteSiteMetadata {
    title
    description
  }
`

export const pageQuery = graphql`
  query SiteQuery {
    site {
      siteMetadata {
        ...SiteInformation
      }
    }
  }
`

export default function Home({ data }) {
  return <div>{data.site.siteMetadata.title}</div>
}

We create the fragment with the query query.

We create the fragment for the SiteSiteMetadata type, which has the website’s metadata fields.

Then the pageQuery uses the fragment we just created.

Querying Data Client-Side with fetch

We can query data on the client-side with the Fetch API.

For instance, we can write:

import React, { useState, useEffect } from "react"

const IndexPage = () => {
  const [starsCount, setStarsCount] = useState(0)
  useEffect(() => {
    fetch(`https://api.github.com/repos/gatsbyjs/gatsby`)
      .then(response => response.json())
      .then(resultData => {
        setStarsCount(resultData.stargazers_count)
      })
  }, [])

  return (
    <section>
      <p>Gatsby start count: {starsCount}</p>
    </section>
  )
}
export default IndexPage

to get the number of Github stars for the Gatsby project and display it.

Images

We can add images into our Gatsby project.

For example, we can write:

src/pages/index.js

import React from "react"
import LaptopImg from "../assets/laptop.jpg"

const IndexPage = () => {
  return (
    <section>
      <img src={LaptopImg} alt="laptop" />
    </section>
  )
}
export default IndexPage

We import the image from the /assets folder and set the src prop to the imported image.

Reference an Image from the static Folder

Also, we can reference an image from the static folder by its path.

For example, we can write:

import React from "react"

const IndexPage = () => {
  return (
    <section>
      <img src={`laptop.jpg`} alt="laptop" />
    </section>
  )
}
export default IndexPage

given that we have the image in the static folder.

Optimizing and Querying Local Images with gatsby-image

We can use the gatsby-image to add an image.

To install the packages required packages, we run:

npm i gatsby-plugin-sharp gatsby-transformer-sharp

To do this, we write:

gatsby-config.js

const path = require('path');

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: path.join(__dirname, `src`, `images`),
      },
    },
    `gatsby-plugin-sharp`,
    `gatsby-transformer-sharp`,
  ],
}

Then we can get the images on our page by writing:

src/pages/index.js

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const IndexPage = () => {
  const data = useStaticQuery(graphql`
    query {
      allFile(
        filter: {
          extension: { regex: "/(jpg)|(png)|(jpeg)/" }
          relativeDirectory: { eq: "" }
        }
      ) {
        edges {
          node {
            base
            childImageSharp {
              fluid {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  `)

  return (
    <div>
      {data.allFile.edges.map(image => (
        <Img
          fluid={image.node.childImageSharp.fluid}
          alt={image.node.base.split(".")[0]}
        />
      ))}
    </div>
  )
}
export default IndexPage

We get the files with the allFile query.

We get the images from the src/images folder as we specified in gatsby-config.js .

Conclusion

We can filter items with GraphQL and we can get images and display them with a query with GraphQL.