在這一篇內容中,咱們一塊兒來看一下其餘的Hooks在業務中的使用。
首先,咱們先來研究一下Todolist根組件的實現,首先,TodoList根組件須要利用useTodolistStoreContext來獲取到items和actions,由於若是items在初始狀態沒有數據的話,須要調用actions中的getInitialItems方法來獲取數據:javascript
const TodoListPage = (props) => {
const [items, actions] = useTodolistStoreContext('items')
useEffect(() => {
if (!items.length) actions.getInitialItems()
})
// ... 忽略其餘
}
複製代碼
此時咱們看到useEffect這個Hook,它是作什麼的呢?很簡單,useEffect其實就能夠理解爲函數組件的生命週期函數,由於函數組件不是類組件,沒法使用componentDidMount等生命週期鉤子,因此Hook提供useEffect Hook來監聽數據的變化,來替代生命週期鉤子,它實際上是componentDidMount、componentDidUpdate、componentWillUnmount三個鉤子函數的組合體,用起來確實方便來不少,可是類組件能夠是有十來個鉤子的,就算useEffect能頂三個,可以用嗎?真的夠用了,不信一塊兒來看:java
有這些就足夠了呢,並且useEffect能夠屢次使用,每次第二個參數傳入監聽的數據後,能夠根據監聽的數據是否變化決定是否執行,多麼智能。react
const Example = (props) => {
let [count, setCount] = useState(0)
// 這樣,初始化的時候,effect會執行,並且不管是count變化仍是props變化,effect也都會執行
useEffect(() => {
})
// 這樣表示下面的這個effect什麼都不監聽,初始的執行一次後,無論props仍是state變化都再也不執行了
useEffect(() => {
}, [])
// 除了初次執行,props變化會執行
useEffect(() => {
}, [props])
// 除了初次執行,props.title變化會執行
useEffect(() => {
}, [props.title])
// 除了初次執行,props.title變化會執行,並且count變化也會執行
useEffect(() => {
}, [props.title, count])
}
複製代碼
嘖嘖嘖,真是甩往常的update階段的鉤子函數五條街都不知道,好比若是咱們想要在某個數據變化後纔去作某個動做,在往常的鉤子中須要手動判斷該數據是否變化: if (state.a !== this.state.a) { ..... }, 而useEffect的使用就簡單多了,傳個參數就能夠了。api
咱們上面的代碼也很簡單,就是當items數據沒有的時候就調用actions的方法去獲取下初始的數據。數組
接下來由於要編輯item,因此咱們須要一個用來存放編輯的item的狀態,並且爲了控制TodoListCAE組件(左側劃出的抽屜)顯示隱藏,因此也須要一個狀態來控制,那麼在TodoList這樣的函數組件中如何構建一個狀態呢?antd
useState這個Hook終於閃亮登場了,它的使用很是簡單,前面以及介紹過了。dom
下面直接放出最終的TodoList根組件:函數
import { useTodolistStoreContext } from '@/hooks/todolist'
import TodoListContent from './particles/TodoListContent'
import TodoListDes from './particles/TodoListDes'
import TodoListCAE from './particles/TodoListCAE'
const TodoListPage = (props) => {
// 取出store的items數據和actions
const [items, actions] = useTodolistStoreContext('items')
// 創建控制右側抽屜顯示的狀態,默認值爲false
let [visible, setVisible] = useState(false)
// 創建用來編輯的item,默認值爲null
let [editItem, setEditItem] = useState(null)
// 當items數據沒有的時候就去初始的獲取
useEffect(() => {
if (!items.length) actions.getInitialItems()
})
// 右側抽屜顯示隱藏動做
function toggleVisible () {
setVisible((visible) => !visible)
setEditItem(null)
}
// 編輯動做
function editAction (item) {
setVisible((visible) => !visible)
setEditItem(item)
}
return (
<div className="page-container todolist-page">
<Typography>
<Typography.Title level = {3}>待辦事項</Typography.Title>
<TodoListDes/>
{/* 新建按鈕 */}
<Button onClick={toggleVisible} className="create-btn" type="primary" shape="circle" icon="plus" size="large" />
</Typography>
{/* 新建和編輯的右側抽屜 */}
<TodoListCAE editItem={editItem} visible={visible} toggleVisible={toggleVisible}/>
{/* 顯示items的內容 */}
<TodoListContent editAction={editAction} toggleVisible={toggleVisible}/>
</div>
)
}
export default TodoListPage
複製代碼
TodoListContent組件也很簡單,由於須要實現分頁,因此也須要構建page狀態來控制當前的頁數:ui
import { useTodolistStoreContext } from '@/hooks/todolist'
import TodoListItem from './TodoListItem'
const TodoListContent = memo((props) => {
// 取出items待用
let [items] = useTodolistStoreContext('items')
// 構建page狀態表明當前頁數,默認值爲1
let [page, setPage] = useState(1)
// 每頁8條數據
let pageSize = 8
// 根據page、pageSize及items來渲染TodoListItem組件的方法
function renderItems () {
// 沒有數據顯示爲空
if (!items.length) return <Empty />
// 準備渲染的分頁後的items
let renderItems = items.slice(
(page - 1) * pageSize,
page * pageSize
)
return renderItems.map((item) => (
<Col className="todolist__item-col" key={item.id} span={24 / (pageSize / 2)}>
<TodoListItem editAction={props.editAction} info={item}/>
</Col>
))
}
return (
<div className="todolist-content">
<Row gutter={16} className="todolist-content__container">
<Suspense fallback={<Spin/>}>{ renderItems() }</Suspense>
</Row>
<Pagination
total={items.length}
showTotal={(total, range) => `${range[0]}-${range[1]} of ${total} items`}
pageSize={pageSize}
current={page}
onChange={(page) => setPage(page)}
/>
</div>
)
})
export default TodoListContent
複製代碼
TodoListItem組件更多的是數據的展現,它須要接受到對於item的信息,獲得items,還須要接受到父級組件傳入的編輯item的動做,以及actions中完成、刪除item的方法:this
import { useTodolistStoreContext } from '@/hooks/todolist'
const TodoListItem = memo((props) => {
const [state, actions] = useTodolistStoreContext()
// item的信息
let { title, description, finished, id } = props.info
// 根據是否完成來肯定主題顏色
let color = finished ? '#1890ff' : '#ccc'
// 完成此todo
function finishAction () {
actions.finishTodo({ id })
}
// 刪除此todo
function deleteAction () {
actions.deleteTodo({ id })
}
// 編輯此todo
function editAction () {
props.editAction(props.info)
}
return (
<Card
className={`todolist__item ${ finished ? 'finished' : '' }`}
title={ title }
extra={<Icon type="check-circle" style={{ fontSize: '24px', color }}/>}
actions={[
<Icon onClick={finishAction} type="check" key="check" />,
<Icon onClick={editAction} type="edit" key="edit" theme="filled" />,
<Icon onClick={deleteAction} type="delete" key="delete" theme="filled" />,
]}
>
<Typography.Paragraph ellipsis={{ rows: 5 }}>{ description }</Typography.Paragraph>
</Card>
)
})
export default TodoListItem
複製代碼
還有一個TodoListDes.js組件,這個組件只須要展現一些計數信息便可:
import { useTodolistStoreContext } from '@/hooks/todolist'
const TodoListDes = memo(() => {
let [detail] = useTodolistStoreContext('todoDetail')
return (
<Typography.Paragraph> { detail.description } </Typography.Paragraph> ) }) export default TodoListDes 複製代碼
Ok,接下來咱們來看一下TodoListCAE.js的實現
import React, { useEffect, useState, useRef } from 'react'
import { Button, Drawer, Form, Input } from 'antd';
import { useTodolistStoreContext } from '@/hooks/todolist'
const { TextArea } = Input
// 這是咱們的表單組件,專門用來新建和編輯item
const TodoListFormUI = (props) => {
const [state, actions] = useTodolistStoreContext()
const { toggleVisible, form } = props
const { getFieldDecorator } = form
// 取消的動做
function handleCancel () {
toggleVisible()
form.resetFields()
}
// 保存的動做
function handleOk () {
// 保存的時候進行數據驗證
form.validateFields((err, values) => {
if (!err) {
// 數據驗證經過後,若是editItem不存在說明是新建模式
if (!props.editItem) {
// 調用actions中的新建方法
actions.createTodoItem({ item: values })
} else {
// 若是是編輯的話,調用actions中的編輯動做
actions.updateTodoItem({ item: { ...values, id: props.editItem.id } })
}
// 完成後隱藏抽屜
toggleVisible()
// 重置表單
form.resetFields()
}
})
}
// 監聽editItem的變化
useEffect(() => {
// 若是editItem存在,說明是要編輯了
if (props.editItem) {
// 取出要編輯的item的信息,同步到form的input value值
let { title, description } = props.editItem
form.setFieldsValue({ title, description })
} else {
// 新建模式的時候重置表單
form.resetFields()
}
}, [props.editItem])
return (
<Form layout="vertical">
<Form.Item label="Title">
{getFieldDecorator('title', {
rules: [{ required: true, message: '事項標題不能爲空!' }],
})(<Input />)}
</Form.Item>
<Form.Item label="Description">
{getFieldDecorator('description', {
rules: [{ required: true, message: '事項內容不能爲空!' }],
})(<TextArea autosize={{ minRows: 15 }} type="textarea" />)}
</Form.Item>
<Form.Item >
<Button onClick={handleOk} type="primary" >
保存
</Button>
<Button type="default" onClick={handleCancel}>
取消
</Button>
</Form.Item>
</Form>
)
}
const TodoListForm = Form.create({ name: 'todolist-form' })(TodoListFormUI)
// 新建和編輯Item的組件
const TodoListCAE = (props) => {
// 接受到的是否隱藏的狀態和切換隱藏狀態的動做
const { visible, toggleVisible } = props
// 抽屜上方展現的title狀態
let [title, setTitle] = useState('')
// 準備標記下面的TodoListForm,標記後放到form中去
const form = useRef(null)
// 根據editItem的變化肯定是編輯模式仍是新建模式,而後更新title
useEffect(() => {
setTitle(props.editItem ? '編輯待辦事項' : '新增一條待辦事項')
// 根據編輯模式仍是新建模式來設置表單的值或者重置表單
// if (form.current) {
// if (props.editItem) {
// let { title, description } = props.editItem
// form.current.setFieldsValue({ title, description })
// } else {
// form.current.resetFields()
// }
// }
})
return (
<Drawer
width="500"
title={title}
placement="right"
closable={false}
onClose={toggleVisible}
visible={visible}
>
<TodoListForm editItem={props.editItem} ref={form} toggleVisible = {toggleVisible}/>
</Drawer>
)
}
export default TodoListCAE
複製代碼
你們能夠看到在TodoListCAE中有useRef鉤子的使用,這個專門用來作ref標記的,在類組件中咱們只須要將標記的元素或者子組件放入到this上,例如:
return (
<div>
<input ref = {inp => this.inp = inp} />
<Child ref = {child => this.child = child}/>
</div>
)
複製代碼
而後就能夠在任意的鉤子函數中利用this.inp和this.child來獲取到標記的input-dom元素和Child子組件了,可是在函數組件中沒有this怎麼掛載呢?沒錯,就是用useRef來生成一個標記數據,而後將要掛載的子組件或Dom元素掛載在這個標記數據上就能夠了。
上面在TodoListCAE中咱們能夠利用useRef構建一個form標記,而後用form來標記表單子組件,而後就能夠在子組件中調用form.XXX來調用到子組件的一些API方法了。
註釋掉是由於沒有這麼作的必要(雖然能夠這麼幹),咱們在Form子組件中本身監聽數據變化來調用重置和設置值的api方法就能夠了。
到這裏位置,咱們的Todolist實例也就編寫完成了,咱們一共研究了useState/useEffect/useReducer/useRef/Custom Hook的實現方式,小夥伴們也要加油喲,其實這些東西都在官方文檔上,要好好看文檔喲。
後續還有其餘有關於React Hook使用方面的幹活我會慢慢放上來的,再見啦。