Alex Liang

React初探 以Todo app為例

最近有個功能需要前端的畫面,功能不複雜而且是給內部使用,趁這個機會玩一下React讓腦袋有不同的刺激。

同事丟給我教學影片,由實作一個可新增/刪除的todo list app開始,範例的程式碼在這裡

產生 React App

首先,使用create-react-app 這個npm module幫忙生成app的骨架

1
2
3
4
5
6
npm install -g create-react-app

create-react-app todo-list

cd todo-list
npm start

在瀏覽器輸入 http://localhost:3000/ 即可看到 app 的畫面

解析程式碼

App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, { Component } from 'react';
import './App.css';
import Header from './components/header';

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

this.addTodo = this.addTodo.bind(this);
this.removeTodo = this.removeTodo.bind(this);
}

addTodo(todoText) {
console.log(`Add ${todoText}`);
}

removeTodo(id) {
console.log(`Removing ${id}`);
}

render() {
return (
<div className="App">
<div className="todo-wrapper">
<Header />
</div>
</div>
);
}
}

export default App;

1~3行將需要的module和css檔案載入專案

Class App 繼承React.Component, 所以在constructor裡需要先呼叫super才能使用父類別的成員函式 super的說明

接下來需要綁定 addTodo 和 removeTodo這二個成員函式讓之後的操作能正確使用bind的說明 Autobinding

render function將我們想呈現的畫面渲染出來,這也是React.Component中必需要實作的函式。其中,Header放在另一個js檔案裡

header.js
1
2
3
4
5
6
7
8
9
import React from 'react';

export default class Header extends React.Component {
render() {
return (
<h1>React Todo</h1>
);
}
}

JSX

看到這裡,相信有學過rails的人一定會對React的邏輯感覺到有所不同

在rails的view裡,前端基本上還是HTML的架構為主;而React則是以js搭配JSX操作component以呈現畫面

todoItem.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import './todoItem.css';

export default class TodoItem extends React.Component {
removeTodo(_id) {
this.props.removeTodo(_id);
}

render() {
return (
<div className="todoWrapper">
<button className="removeTodo" onClick={(e) => this.removeTodo(this.props.id)}>remove</button>{this.props.todo.text}
</div>
);
}
}

上述程式碼用來處理todo list的各個事項。在render裡可以看到HTML的class被換成className,這是為了和javascript的class做區別

而button的部分,在onClick事件觸發時會執行{(e) => this.removeTodo(this.props.id)}
這是javascript的語法,表示當button被點擊時會呼叫removeTodo,傳入待辦事項的id以移除。在removeTodo function裡又呼叫了this.props.removeTodo(_id),props是用來當作傳入component的參數,也就是說這裡將_id傳入App.js的removeTodo裡

App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import React, { Component } from 'react';
import './App.css';
import Header from './components/header';
import TodoInput from './components/todoInput';
import TodoItem from './components/todoItem';

class App extends Component {
// 略

addTodo(todoText) {
let todos = this.state.todos.slice();
todos.push({
_id: this.state.nextId,
text: todoText
});
const nextId = this.state.nextId + 1;
this.setState({
todos,
nextId
});
}

removeTodo(id) {
this.setState({
todos: this.state.todos.filter(todo => todo._id !== id),
});
}

render() {
return (
<div className="App">
<div className="todo-wrapper">
<Header />
<TodoInput todoText="" addTodo={this.addTodo}/>
<ul>
{
this.state.todos.map((todo) => {
return <TodoItem todo={todo} key={todo._id} id={todo._id} removeTodo={this.removeTodo}/>
})
}
</ul>
</div>
</div>
);
}
}

export default App;

以上就是React的初次介紹,其實學新東西最主要的目的不是為了跟上潮流,而是讓自己的腦袋活化,把其它知識做比較和整理並擴展認知邊界。

參考資料: