教程来自freecodeCamp:【英字】使用 React 和 TypeScript 构建应用程序
跟做,仅记录用
其他资料:https://www.freecodecamp.org/chinese/news/learn-typescript-beginners-guide/
作者提供的源码https://github.com/piyush-eon/react-typescript-taskify/tree/react-typescript-tutorial
第四天
以下是视频(1:00-1:20) 的内容
1 useReducer
这part的代码是我自己写的,因为原视频中说作为homework,就没给代码
对todo内容的增删改查进行逻辑整合,写一个reducer
state
即为todo数组
action
有四种: add、remove、done、edit
改造成reducer
- 先定义针对todos的reducer函数
TodoReducer
,以及Actions
类型
model.ts
// ......
export type Actions =
{ type: 'add', payload: string }
| { type: 'remove', payload: number }
| { type: 'done', payload: number }
| { type: 'edit', payloadId: number, payloadContent: string}
export const TodoReducer = (state: Todo[], action: Actions) => {
switch (action.type) {
case 'add':
return [
...state,
{ id: Date.now(), todo: action.payload, isDone: false }
];
case 'remove':
return state.filter((todo) => todo.id !== action.payload);
case 'done':
return state.map((todo) => todo.id === action.payload
? {...todo, isDone: !todo.isDone}
: todo
)
case 'edit':
return state.map((todo) => (
todo.id === action.payloadId
? {...todo, todo: action.payloadContent }
: todo
))
default:
return state;
}
}
- 其次修改 App.tsx,用
useReducer
替换useState
,修改handleAdd
以及向子组件的传值
import React, { useState, useReducer } from 'react';
import './App.css';
import InputField from './components/InputField';
import TodoList from './components/TodoList';
import { TodoReducer } from "./model";
const App: React.FC = () => {
const [todo, setTodo] = useState<string>(""); // 尖括号加上变量类型
const [todosState, dispatch] = useReducer(TodoReducer, []);
return (
<div className="App">
<span className='heading'>Taskify</span>
<InputField todo={todo} setTodo={setTodo} todosDispatch={dispatch}/>
<TodoList todosState={todosState} todosDispatch={dispatch}/>
</div>
);
}
export default App;
}
- 在子组件中使用
dispatch
函数,先看‘add’这个action的调用,即InputField.tsx
// ...
type Props = {
todo: string,
setTodo: React.Dispatch<React.SetStateAction<string>>,
todosDispatch: React.Dispatch<Actions>,
}
const InputField: React.FC<Props> = ({todo, setTodo, todosDispatch}: Props) => {
const inputRef = useRef<HTMLInputElement>(null);
// 点击GO后
const handleAdd = (e: React.FormEvent):void => {
e.preventDefault(); // 取消默认的页面刷新行为
if(todo) {
todosDispatch({type: 'add', payload: todo})
setTodo("");
}
};
return (
<form className='input' onSubmit={(e) => {
handleAdd(e);
console.log(e);
inputRef.current?.blur(); // 移除focus状态
}}
>
<input type="input"
ref={inputRef}
value={todo}
onChange={
(e)=>setTodo(e.target.value)
}
placeholder="Enter a task"
className='input__box'
/>
<button className='input_submit' type='submit'>
GO
</button>
</form>
)
}
- 再修改使用done、remove、edid的SingleTodo.tsx
type Props = {
todo: Todo,
todosDispatch: React.Dispatch<Actions>
}
const SingleTodo = ({todo, todosDispatch}: Props) => {
const [edit, setEdit] = useState<boolean>(false);
const [editTodo, setEditTodo] = useState<string>(todo.todo);
// done按钮的响应
const handleDone = (id: number) => {
todosDispatch({
type: 'done',
payload: id
})
};
// edit按钮的响应
const handleEdit = (e:React.FormEvent, id: number) => {
e.preventDefault(); // 禁止默认的页面刷新行为
todosDispatch({
type:'edit',
payloadContent: editTodo,
payloadId: id
})
setEdit(false);
}
// delete按钮的响应
const handleDelete = (id: number ) => {
todosDispatch({
type: 'remove',
payload: id
})
};
const inputRef = useRef<HTMLInputElement>(null); // 编辑框
// edit状态改变时自动获取编辑框的焦点
useEffect(() => {
inputRef.current?.focus();
}, [edit]);
// ...
}
至此,完成了基础版的TASKIFY
2 增加分区功能
我们进一步完善,期望将进行中的任务和已完成的任务分区显示
更改TodoList.tsx中的HTML结构
const TodoList:React.FC<Props> = ({todosState, todosDispatch}: Props) => {
return (
<div className="container">
<div className='todos'>
<span className="todos__heading">
Active Tasks
</span>
{
todosState.map(todo =>(
<SingleTodo key={todo.id}
todo={todo}
todosDispatch={todosDispatch}
/>
))}
</div>
<div className='todos remove'>
<span className="todos__heading">
Completed Tasks
</span>
{todosState.map(todo =>(
<SingleTodo key={todo.id}
todo={todo}
todosDispatch={todosDispatch}
/>
))}
</div>
</div>
)
}
修改样式 styles.css
.input {
display: flex;
width: 95%;
position: relative;
align-items: center;
}
.input__box {
width: 100%;
border-radius: 50px;
padding: 20px 30px;
font-size: 25px;
border: none;
transition: 0.2s;
box-shadow: inset 0 0 5px black;
}
.input__box:focus {
box-shadow: 0 0 10px 1000px rgba(0, 0, 0, 0.5);
outline: none;
}
.input_submit {
position: absolute;
border-radius: 50%;
font-size: 15px;
margin: 12px;
right: 0px;
background-color: #2f74c0;
color: white;
width: 50px;
height: 50px;
border: none;
box-shadow: 0 0 10px black;
transition: 0.2s all;
}
.input_submit:hover {
background-color: #388ae2;
}
/* 按钮点击时 */
.input_submit:active {
transform: scale(0.8); /* 缩放 */
box-shadow: 0 0 5px black;
}
.container {
display: flex;
width: 95%;
margin-top: 10px;
align-items: flex-start;
justify-content: space-between;
}
.todos {
display: flex;
width: 47.5%;
flex-direction: column;
padding: 15px;
border-radius: 5px;
background-color: rgb(50, 195, 205);
}
.todos__heading {
font-size: 20px;
color: white;
}
.todos__single {
display: flex;
border-radius: 5px;
padding: 20px;
margin-top: 15px;
background-image: url("https://img.freepik.com/free-photo/crumpled-yellow-paper-background-close-up_60487-2390.jpg?size=626&ext=jpg");
transition: 0.2s;
}
.todos__single:hover {
box-shadow: 0 0 5px black;
transform: scale(1.03);
}
.todos__single--text {
flex: 1;
padding: 5px;
border: none;
font-size: 20px;
}
.todos__single--textJ:focus {
outline: none;
}
.icon {
margin-left: 10px;
font-size: 25px;
cursor: pointer;
}
@media (max-width: 1100px){
.todos {
width: 45%;
}
}
@media (max-width: 700px) {
.input {
width: 95%;
}
.todos {
width: 95%;
margin-bottom: 10px;
}
.container {
width: 95%;
flex-direction: column;
}
}
.remove {
background-color: rgb(235, 103, 80);
}
在App.tsx中为TodoList组件传入completedTodos
属性
<TodoList
todosState={todosState}
todosDispatch={dispatch}
completedTodos={completedTodos}
setCompletedTodos={setCompletedTodos}
/>
3 添加两个列表间的拖拽功能
借助第三方包 react-beautiful-dnd
ps: 在TypeScript中安装一些依赖时,需要安装types版本,注意不要安装成js包了
npm install --save @types/react-beautiful-dnd
… 得,这个依赖我整不好了,应该是各依赖版本间兼容的问题,总是报错。
具体这个依赖的使用,官方也有详细教程,之后再看吧…
总结
今天主要是useReducer
的使用。react-beautiful-dnd那个包可气死我了,以后再说吧。
这个小项目就结束了,主要是为了熟悉一下TypeScript,顺便复习React和CSS,还留了个小坑
视频作者提供了项目源码,供学习https://github.com/piyush-eon/react-typescript-taskify/tree/react-typescript-tutorial
本博仅做记录,如有问题,都是我的错,我改正