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
component
undersrc
, which containtodo-list
folder inside it. Intodo-list
createindex.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.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"
- 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;
- (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
welcome
folder and create new folder namedtodo
underpage
folder, which containindex.js
,todo.js
, andtodo.css
underpage
folder. 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.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.
- 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
id
we defined onFormattedMessage
before. We need to put it on every./src/_locale/<locale>.json
we define. In this case,id.json
anden.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
values
props 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!