Treats

Treats

  • Getting Started

›Tutorial

Getting Started

  • Installation

Tutorial

  • 01. Creating Your First Page
  • 02. Using Redux
  • 03. Using GraphQL
  • 04. Fetch Data for SSR
  • 05. Adding Addons

Main Concepts

  • Overview
  • Routing
  • Localization
  • Code-splitting
  • Redux
  • GraphQL Client
  • Middlewares
  • Helpers
  • Server-side Events
  • Server-side Template
  • Server-side Rendering
  • Custom Server App
  • Custom Client Initialization
  • Custom React App
  • Runtime Config
  • Build Config
  • Environment Variables
  • Code Generator
  • Scripts
  • Addons
  • Typescript
  • Workbox

API Reference

  • Overview
  • Filesystem Hooks
  • Components
  • Server
  • Client
  • Router
  • Intl
  • Locale Data
  • Helmet
  • Redux
  • Graphql

Authoring Addons

  • Overview
  • Helpers
  • Middlewares
  • Generators
  • Wrapping Up

Addons

  • Treats Addons List

Contributing

  • How To Contribute

FAQ

  • FAQs

01. Creating Your First Page

Treats uses React as it's core. If you don't get a grip on React concept, we suggest you to learn more about React before following this tutorial.

In this section, we will create a simple todo-apps using Treats. To create Treats app, install create-treats-app packages and run create-treats-app on you terminal.

# yarn
~$ yarn global add create-treats-app

# npm
~$ npm install -g create-treats-app

~$ create-treats-app

A prompt asking project's name, version, description, and usage of Typescript will be shown. Please fill it all or just enter if you want it to be filled with the default values. Before we start, let's assume we want to create a page named todo.

Creating Component

Let's make our list in separate component.

  1. Create a folder component under src, which contain todo-list folder inside it. In todo-list create index.js, todo-list.js, and todo-list.css (optional). Your folder structure should be:
src
|-- _locale
|-- _route
|-- component
    |-- todo-list
        |-- index.js
        |-- todo-list.css
        |-- todo-list.js
|-- page
  1. Write our index.js file:
/* src/component/todo-list/index.js */

import TodoList from "./todo-list";

export default TodoList;

Trivia

At this point, you may be wondering why we need index.js that only import the component and export it. To put it simple, it will make our component import easier. Here are the difference:

//with index.js
import Todo from "./src/component/todo"

//without index.js
import Todo from "./src/component/todo/todo"
  1. Write our todo-list.js file:
/* src/component/todo-list/todo-list.js */

import React from "react";

const TodoList = ({items}) => (
    <ul>
        {items && items.map((item, index) => (
            <li key={index}>
                {item}
            </li>
        ))}
    </ul>
);

export default TodoList;
  1. (Optional) Style your component in todo-list.css.

The easier way

Treats can generate components boilerplate using generators, so we can jump to step 3. The commands:

# yarn
~$ yarn generate component

# npm
~$ npm run generate component

prompt: Component directory:  (./src/component) #directly press enter or change it to another location you preffered
prompt: Component filename: (my-component) todo-list
prompt: Component variable name: (MyComponent) TodoList
prompt: Component description: (My Treats Component) #Fill it with what you want

prompt: Component type (Functional|ClassComponent|ClassPureComponent):  (Functional) #directly press enter for functional component

prompt: Connect your component to redux? (true (t)|false (f)):  (false) #skip it for now, press enter
prompt: Code split your component (true (t)|false (f)):  (false) #skip it for now, press enter

prompt: Component props (CMD+C to enter): items #enter every props you want. Press CTRL + C or CMD + C when done.

prompt: The above files would be overwritten by the generator are you sure? (true (t) | false (f)):  (false) t

Voila! Find your component in the location you typed and continue to step 3 above.

Creating Page

Now that our TodoList component is done, now let's dive into our page.

  1. Remove default page welcome folder and create new folder named todo under page folder, which contain index.js, todo.js, and todo.css under page folder. Your folder should look like this:
page
|-- todo
    |  
    |-- index.js
    |-- todo.js  
    |-- todo.css  
/* index.js */
import Todo from "./todo";

export default Todo;
  1. Write todo.js for your page
/* ./src/page/todo/todo.js */

import React, { Component } from "react";

import TodoList from "../../component/todo-list";

import style from "./todo.css";

class Todo extends Component {
    constructor(props) {
        super(props);

        this.state = {
            keyword: "",
            items: []
        }
    }

    handleFormSubmit = e => {
        e.preventDefault();

        const { keyword, items } = this.state;

        this.setState({
            keyword: "",
            items: [...items, keyword]
        });
    }

    handleInputChange = e => {
        this.setState({
            ...this.state,
            keyword: e.target.value
        })
    }

    render() {
        const { keyword, items } = this.state;
        return (
            <div className={style.todo_form}>
                <form onSubmit={this.handleFormSubmit}>
                    <input value={keyword} onChange={this.handleInputChange} />
                    <button>Submit</button>
                </form>
                <TodoList items={items} />
            </div>
        )
    }
}

export default Todo;

Here in todo.js, we will create a form consist of input text, submit button, and the todo-list we create earlier. Once, the user submit a todo items, todo list will display the items immediately.

  1. Style your page in todo.css.
.todo_form {
    background-color: #e0e0e0;
    padding: 10px;
}

Register Our Page To Route

Since we remove default welcome page, we need to rename some variables in _route.

  1. Change your variable name "WELCOME" into "TODO", in src/_route/path.js.
/* src/_route/path.js */

export const TODO = "/";
  1. Change the all "WELCOME" into "TODO" in /src/_route/route.js. You also need to change the "name" props, so it match the variable name we defined earlier.
/* src/_route/route.js */

import { TODO } from "./path";

const route = [
    {
        name: "todo",
        path: TODO,
        exact: true,
        disabled: true
    }
];

export default route;

You may see some weird configuration out there like disable and exact. These are Route props from react-router. Check the documentation for another configuration.

  1. Last but not least, change all "WELCOME" into "TODO" and the page import from "@page/welcome" to "@page/todo". Don't forget to rename "Welcome" to "Todo".
/* src/_route/module.js */

import Todo from "@page/todo";

import { TODO } from "./path";

const module = {
    [TODO]: Todo
};

export default module;
  1. Congratulations! You create your first Treats page! To check your page, open terminal and proceed to project's root directory, then run it.
# yarn
~$ yarn start

# npm
~$ npm start
  1. A browser will open automatically on http://localhost:3000. If your browser is not opening, then open your browser and browse for http://localhost:3000. The page should appear on your browser.

Note

Your port may not be 3000. This could happen because the default port (3000) is already been used. Check the default port when you start the project

Localization Guide

Sometimes you will need to build your app in multiple languages. Of course you won't create two or more page with different language, right? Treats have solution for the case, Localization! Our localization is based on React Intl. Let's implement some localization to our first page.

  1. Let's get back to our todo.js, and add some text using <FormattedMessage /> or intl.formatMessage().
/* todo.js */
import React, { Component } from "react";
import { FormattedMessage, injectIntl } from "@treats/intl";

import TodoList from "../../component/todo-list";

import style from "./todo.css";

class Todo extends Component {
    constructor(props) {
        super(props);

        this.state = {
            keyword: "",
            items: []
        }
    }

    ...

    render() {
        const { keyword, items } = this.state,
            { intl } = this.props;
        return (
            <div className={style.todo_form}>
                <FormattedMessage id="todo_title" />
                <form onSubmit={this.handleFormSubmit}>
                    <input value={keyword} onChange={this.handleInputChange} />
                    <button>{intl.formatMessage({id: "submit"})}</button>
                </form>
                <TodoList items={items} />
            </div>
        )
    }
}

export default injectIntl(Todo);
  1. Put some strings with the corresponding id we defined on FormattedMessage before. We need to put it on every ./src/_locale/<locale>.json we define. In this case, id.json and en.json.
/* id.json */
{
    .......
    "todo_title": "Rencana Saya",
    "submit": "Simpan"
}
/* en.json */
{
    .......
    "todo_title": "My Todo List",
    "submit": "Submit"
}
  1. We can also inject some value into our string. To inject some value, use values props on FormattedMessage, and add a placeholder in our locale json.
/* todo.js */
class Todo extends Component {

    ...

    render() {
        const { keyword, items } = this.state,
            { intl } = this.props;
        return (
            <div className={style.todo_form}>
                <FormattedMessage 
                    id="todo_title" 
                    values={{
                        name: "User",
                        value2: 20
                    }}
                />
                ...
            </div>
        )
    }
...
/* id.json */
{
    .......
    "todo_title": "Rencana Saya. Nama saya: {name}. Ini value2: {value2}",
    "submit": "Simpan"
}
/* en.json */
{
    .......
    "todo_title": "My Todo List. My name is: {name}. This is value2: {value2}",
    "submit": "Submit"
}
  1. We'll now create a switch to change selected languages.
/* todo.js */
class Todo extends Component {

    ...

    render() {
        const { keyword, items } = this.state,
            { intl } = this.props;
        return (
            <div className={style.todo_form}>
                <FormattedMessage 
                    id="todo_title" 
                    values={{
                        name: "User",
                        value2: 20
                    }}
                />
                <TodoList items={items} />
                <div className={style.todo__locale_switcher_container}>
                    <a
                        className={`${style.todo__locale_switcher}${
                            intl.locale === "en" ? " active" : ""
                        }`}
                        href="/?lang=en"
                    >
                        English
                    </a>
                    <a
                        className={`${style.todo__locale_switcher}${
                            intl.locale === "id" ? " active" : ""
                        }`}
                        href="/?lang=id"
                    >
                        Indonesian
                    </a>
                </div>
        )
    }
...
/* todo.css */
...

.todo__locale_switcher_container {
    position: relative;
    white-space: nowrap;
    padding-top: 20px;
    font-size: 12px;
}

.todo__locale_switcher {
    display: inline-block;
    vertical-align: middle;
    padding: 10px;
    min-width: 100px;
    background: #fff;
    color: #42b549;
    border: 1px solid #f0f0f0;
}

.todo__locale_switcher:global(.active) {
    background: #42b549;
    color: #fff;
}

.todo__locale_switcher:first-child {
    border-top-left-radius: 30px;
    border-bottom-left-radius: 30px;
}

.todo__locale_switcher:last-child {
    border-top-right-radius: 30px;
    border-bottom-right-radius: 30px;
}

.todo__locale_switcher:global(.active):hover {
    color: #FFF;
}
  1. That's it! Now you can do the localization for your page!
← Installation02. Connecting Components with Redux →
Tokopedia Open Source
Copyright © 2019 Tokopedia OSS