昨天將 React Todo 的殼寫出來了,今天就把之前做的 Firebase 當作心臟給放進去,讓它活起來吧!
事前準備
yarn add firebase
dev server
=> yarn start
Dan Abramov <- react 帥哥
在 twitter 分享他畫的 lifecycle 圖
最左邊是 Mounting,就是 react 第一次做 component 的時候會經過的地方
在結束的時候會呼叫 componentDidMount
這個 method
官方文件有說在這邊開發者可以呼叫 setState
來進行更新
只要 setState
被呼叫(或收到新的 props)就會進入圖上的 updating
狀態
使用這幾個重點就可以給予 TodoList 生命了!
使用 state 和 componentDidMount
class
state = {
list: [],
isLoading: false,
}
componentDidMount() {
// 將狀態更新成 isLoading
this.setState({
isLoading: true,
})
FirebaseService.getTodoList()
.then((list) => {
// 將讀取的列表放進更新裡!然後將 isLoading 改為 false
this.setState({
list,
isLoading: false,
})
});
}
render function 改成
render() {
const {
list,
isLoading,
} = this.state;
return (
<div className="container">
<ActionController
handleAdd={this.handleAdd}
/>
<LoadingSpinner isLoading={isLoading} />{/* 是否讀取中 */}
<TodoList
list={list}
handleUpdate={this.handleUpdate}
handleDelete={this.handleDelete}
/>
</div>
)
}
在 class component 裡面新增了
這三個 function
來控制更新刪除與新增,分別帶入 TodoList 和 ActionController 元件
handleUpdate = (id, item) => {
this.setState({
isLoading: true,
})
FirebaseService.updateTodo(id, item)
.then((res) => {
// PureComponent will not work!
this.setState(({list}) => {
return {
list,
isLoading: false,
}
})
})
}
可以使用 setState 來讓底下進行更新,但是這樣寫會讓 PureComponent
不能進行更新
因為 PureComponent
會在 shouldComponentUpdate
的時候做 props 的 shallow check 然後如果有變動就會進行 Component 的更新
意思是僅檢查物件的 reference 不會檢查物件裡的 property 的變動
所以像是
group = {
a: 'jerry',
b: 'taiming'
}
groupX = group
groupY = group
groupX.c = 'odie'
groupX === groupY // true
會是 true
因為都指向同一個 reference
這樣做 react
可以很快地知道哪個節點需要更新哪些不用更新
所以可以改成
this.setState(({list}) => {
return {
isLoading: false,
list: list.map((item) => {
if (item.id === id) {
return res;
}
return item;
}),
}
})
handleDelete = (id) => {
this.setState({
isLoading: true,
})
FirebaseService.deleteTodo(id)
.then((res) => {
this.setState(({ list }) => {
return {
isLoading: false,
list: list.filter((item) => item.id !== id),
}
})
})
}
handleAdd = (name) => {
this.setState({
isLoading: true,
})
FirebaseService.addTodo(name, false)
.then((res) => {
this.setState(({ list }) => ({
isLoading: false,
list: [...list, res],
}))
})
}
class TodoList extends React.Component {
render() {
const {
list,
handleUpdate,
handleDelete,
} = this.props;
return (
<ul>
{
list.map((item) => (
<TodoItem
key={item.id}
item={item}
handleUpdate={handleUpdate}
handleDelete={handleDelete}
/>
))
}
</ul>
)
}
}
const getCheckedClass = (checked) => checked ? 'list-group-item-dark' : '';
class TodoItem extends React.Component {
handleOnClick = (evt) => {
const {
item,
handleUpdate,
} = this.props;
item.check = !item.check;
handleUpdate(item.id, item);
}
handleOnDelete = (evt) => {
const {
item,
handleDelete,
} = this.props;
// 記得這個因為上層有更新的 code 所以用這個避免刪除時冒泡上去被更新
evt.stopPropagation();
handleDelete(item.id);
}
render() {
const {
item,
} = this.props;
return (
<li
className={`list-group-item list-group-item-action ${getCheckedClass(item.check)}`}
onClick={this.handleOnClick}
>
<span className="todo-content">{item.name}</span>
<span className="close" onClick={this.handleOnDelete}>x</span>
</li>
);
}
}
class ActionController extends React.Component {
inputNode = null;
inputRefSetup = (inputNode) => {
this.inputNode = inputNode
}
handleOnCreateClick = (evt) => {
const {
handleAdd,
} = this.props;
handleAdd(this.inputNode.value)
evt.preventDefault();
}
render() {
return (
<form className="form-inline mt-3 mb-3">
<div className="form-group">
<label className="sr-only">Todo</label>
<input
className="form-control"
ref={this.inputRefSetup}
type="text"
id="todo-input" placeholder="待辦事項"
></input>
</div>
<button
className="btn btn-primary ml-3"
id="addTodo"
onClick={this.handleOnCreateClick}
>新增</button>
</form>
)
}
}
新增的地方我把 input 節點拿出來,在 createClick 的時候把 value
丟給前面處理
今天快速的把 react
部分過完,發現使用元件的概念來寫前端,邏輯處理變得簡單得多了
另外加上了 loading
提示。現在處理 side effect 也是比之前容易得多 了!
今天的 code