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.
- Create a folder
componentundersrc, which containtodo-listfolder inside it. Intodo-listcreateindex.js,todo-list.js, andtodo-list.css(optional). Your folder structure should be:
src
|-- _locale
|-- _route
|-- component
|-- todo-list
|-- index.js
|-- todo-list.css
|-- todo-list.js
|-- page
- Write our
index.jsfile:
/* 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.jsthat 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"
- Write our
todo-list.jsfile:
/* 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;
- (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.
- Remove default page
welcomefolder and create new folder namedtodounderpagefolder, which containindex.js,todo.js, andtodo.cssunderpagefolder. Your folder should look like this:
page
|-- todo
|
|-- index.js
|-- todo.js
|-- todo.css
/* index.js */
import Todo from "./todo";
export default Todo;
- Write
todo.jsfor 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.
- 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.
- Change your variable name "WELCOME" into "TODO", in
src/_route/path.js.
/* src/_route/path.js */
export const TODO = "/";
- 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.
- 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;
- 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
- 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.
- Let's get back to our
todo.js, and add some text using<FormattedMessage />orintl.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);
- Put some strings with the corresponding
idwe defined onFormattedMessagebefore. We need to put it on every./src/_locale/<locale>.jsonwe define. In this case,id.jsonanden.json.
/* id.json */
{
.......
"todo_title": "Rencana Saya",
"submit": "Simpan"
}
/* en.json */
{
.......
"todo_title": "My Todo List",
"submit": "Submit"
}
- We can also inject some value into our string. To inject some value, use
valuesprops onFormattedMessage, 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"
}
- 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;
}
- That's it! Now you can do the localization for your page!
