原文地址:How to Build a Todo List with React Hooks css
React v16.7.0-alpha 引入了鉤子(Hooks)。開心!html
鉤子是能讓你在沒有用es6類的狀況下使用React的狀態, 生命週期鉤子這些特性的功能。react
優點:git
查看更多:React’s official Hooks introes6
寫這篇文章時,鉤子還處於內部測試(alpha)階段。它們的API隨時均可能改變。
我建議你在你的業餘項目中體驗鉤子,在它們成爲穩定版本以前,不要在線上代碼中使用。github
待辦事項清單是使用普遍的例子,理由很充分——它們是很棒的練習工具。不管你想嘗試任何語言或庫我都推薦使用它。
在這個例子中,咱們只實現其中的一小部分功能:npm
這是github和CodeSandbox的地址數組
git clone https://github.com/yazeedb/react-hooks-todo cd react-hooks-todo npm install
master分支已經實現了這些功能,若是你想本身跟着實現,請切到start分支。dom
git checkout start
啓動工程函數
npm start
這個應用應該跑在localhost:3000
上,這是初始UI:
咱們已經設置了material-ui來給頁面一個專業的外觀,如今咱們加入更多功能!
添加一個新文件,src/TodoForm.js
。這是初始代碼:
import React from 'react'; import TextField from '@material-ui/core/TextField'; const TodoForm = ({ saveTodo }) => { return ( <form> <TextField variant="outlined" placeholder="Add todo" margin="normal" /> </form> ); }; export default TodoForm;
經過組件名字,咱們就知道它是用來添加事項的,它也就是咱們的第一個鉤子。
看這段代碼:
import { useState } from 'react'; const [value, setValue] = useState('');
useState
是一個接收初始狀態(state)返回一個數組的函數。console.log
它吧。
數組的第一個值是你的state如今的值,第二個值是state的更新方法。
因此咱們把它們叫作value
和setValue
, 並使用es6解構賦值對它們進行賦值。
咱們的表單應該跟蹤input的值並在保存提交時執行saveTodo
方法。useState
能幫咱們實現它。
更新updateForm.js
, 這是更新以後的代碼:
import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; const TodoForm = ({ saveTodo }) => { <b>const [value, setValue] = useState('');</b> return ( <form <strong>onSubmit={event => {</strong> event.preventDefault(); saveTodo(value); }} > <TextField variant="outlined" placeholder="Add todo" margin="normal" onChange={event => { setValue(event.target.value); }} value={value} /> </form> ); }; export default TodoForm;
回到index.js
,引入而且使用這個組件。
... import TodoForm from './TodoForm'; ... const App = () => { return ( <div className="App"> <Typography component="h1" variant="h2"> Todos </Typography> <TodoForm saveTodo={console.warn} /> </div> ); };
如今你在input輸入的值已經能夠被打印出來了。(記得敲enter哦)
咱們的事項列表todos也須要state。在index.js
中引入useState
。初始state應該是空數組。
import React, { useState } from 'react'; ... const App = () => { const [todos, setTodos] = useState([]); return ...
創建一個新文件:src/TodoList.js
大部分代碼是來自Material-UI庫的高級組件, 這是更新以後的代碼:
import React from 'react'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; import ListItemText from '@material-ui/core/ListItemText'; import Checkbox from '@material-ui/core/Checkbox'; import IconButton from '@material-ui/core/IconButton'; import DeleteIcon from '@material-ui/icons/Delete'; const TodoList = ({ todos, deleteTodo }) => ( <List> {todos.map((todo, index) => ( <ListItem key={index.toString()} dense button> <Checkbox tabIndex={-1} disableRipple /> <ListItemText primary={todo} /> <ListItemSecondaryAction> <IconButton aria-label="Delete" onClick={() => { deleteTodo(index); }} > <DeleteIcon /> </IconButton> </ListItemSecondaryAction> </ListItem> ))} </List> ); export default TodoList;
它接收兩個屬性:
在index.js
中引入這個組件。
... import TodoList from './TodoList'; import './styles.css'; const App = () => { ...
並在App
方法中使用它:
... <TodoForm saveTodo={console.warn} /> <TodoList todos={todos} />
仍是在index.js
中,編輯TodoForm
的屬性, saveTodo
:
<TodoForm saveTodo={todoText => { const trimmedText = todoText.trim(); if (trimmedText.length > 0) { setTodos([...todos, trimmedText]); } }} />
這裏咱們只是把空格去掉,把新的值添加到todos
中。
咱們如今能夠添加事項了!
如今添加新的事項後,咱們沒有把input清空,這是很差的用戶體驗!
咱們只須要在TodoForm.js
中作一點小改動,就能夠修復它。
<form onSubmit={event => { event.preventDefault(); saveTodo(value); setValue(''); }} >
當事項被保存後,咱們就把form的state變成空字符串。
如今看起來很好了!
TodoList
爲每一條事項都提供了索引,根據索引咱們能找到咱們想刪除的事項。
// TodoList.js <IconButton aria-label="Delete" onClick={() => { deleteTodo(index); }} > <DeleteIcon /> </IconButton>
在index.js
中傳遞這個函數
<TodoList todos={todos} deleteTodo={todoIndex => { const newTodos = todos .filter((_, index) => index !== todoIndex); setTodos(newTodos); }} />
咱們使用setTodos
方法把全部不符合index
的事項保存下來。
刪除功能完成!
文章開頭我提到 鉤子便於分離狀態和組件的邏輯。因此咱們在這個應用中能夠這樣作。
新建一個文件叫src/useTodoState.js
import { useState } from 'react'; export default initialValue => { const [todos, setTodos] = useState(initialValue); return { todos, addTodo: todoText => { setTodos([...todos, todoText]); }, deleteTodo: todoIndex => { const newTodos = todos .filter((_, index) => index !== todoIndex); setTodos(newTodos); } }; };
這就是index.js
中原來的代碼,咱們只是把它分離出來了!咱們的狀態處理邏輯再也不和組件混在一塊兒了!
如今咱們只須要引入它,這是更新以後的代碼:
import React from 'react'; import ReactDOM from 'react-dom'; import Typography from '@material-ui/core/Typography'; import TodoForm from './TodoForm'; import TodoList from './TodoList'; import useTodoState from './useTodoState'; import './styles.css'; const App = () => { const { todos, addTodo, deleteTodo } = useTodoState([]); return ( <div className="App"> <Typography component="h1" variant="h2"> Todos </Typography> <TodoForm saveTodo={todoText => { const trimmedText = todoText.trim(); if (trimmedText.length > 0) { addTodo(trimmedText); } }} /> <TodoList todos={todos} deleteTodo={deleteTodo} /> </div> ); }; const rootElement = document.getElementById('root'); ReactDOM.render(<App />, rootElement);
如今一切工做正常!
咱們能夠對錶單作一樣的處理!
新建一個文件:src/useInputState.js
import { useState } from 'react'; export default initialValue => { const [value, setValue] = useState(initialValue); return { value, onChange: event => { setValue(event.target.value); }, reset: () => setValue('') }; };
如今todoForm.js
應該變成這樣:
import React from 'react'; import TextField from '@material-ui/core/TextField'; import useInputState from './useInputState'; const TodoForm = ({ saveTodo }) => { const { value, reset, onChange } = useInputState(''); return ( <form onSubmit={event => { event.preventDefault(); saveTodo(value); reset(); }} > <TextField variant="outlined" placeholder="Add todo" margin="normal" onChange={onChange} value={value} /> </form> ); }; export default TodoForm;
如今咱們所有完成了!
但願你喜歡!!
謝謝!
做者:Yazeed Bzadough