博主介绍:大爽歌, b站小UP主 ,直播编程+红警三 ,python1对1辅导老师 。
本博客为microsoft的的Frontend Bootcamp 的day1中 react 部分的学习笔记
笔记目录: React 入门实例 学习笔记 目录
请先看目录,按照目录先后顺序阅读实践
本博客有对应视频版本,具体见目录。
0 分支检出
使用vs-code
从上一节的分支todoapp-v2
,检出分支todoapp-v3
具体见视频:
【代码过程】 React 入门实例 TodoApp 学习梳理笔记
1 添加typescript
之前的代码虽然都是tsx
格式的,
但其实没有加type
限制,
这里来添加下。
新建src/TodoApp.types.ts
如下,
export type FilterTypes = 'all' | 'active' | 'completed';
export type TodyType = 'active' | 'completed' | 'cleared';
export interface Todo {
id: string;
label: string;
status: TodoType;
}
export type Todos = Todo[];
export type AddTodo = (label: string) => void;
export type ToggleCompleted = (id: string) => void;
export type ClearCompleted = () => void;
export type ChangeFilter = (filter: FilterTypes) => void;
然后修改其他tsx
文件,加上对应的type
限制。
修改后,诸多文件情况如下
src/TodoApp.tsx
如下
import React from 'react';
import {
TodoHeader } from './components/TodoHeader.tsx';
import {
TodoList } from './components/TodoList.tsx';
import {
TodoFooter } from './components/TodoFooter.tsx';
import {
Todo, Todos, FilterTypes } from './TodoApp.types';
const defaultTodos: Todos = [
{
id: '01',
label: 'Todo 1',
status: 'completed',
},
{
id: '02',
label: 'Todo 2',
status: 'active',
},
{
id: '03',
label: 'Todo 2',
status: 'active',
},
{
id: '04',
label: 'Todo 3',
status: 'completed',
},
]
export const TodoApp = () => {
const [filter, setFilter] = React.useState<FilterTypes>('all');
const [todos, setTodos] = React.useState<Todos>(defaultTodos);
const addTodo = (label: string): void => {
const getId = () => Date.now().toString();
const newTodo = {
id: getId(),
label: label,
status: 'active',
};
setTodos([...todos, newTodo]);
}
const toggleCompleted = (id: string) => {
const newTodos = todos.map((todo): Todo =>{
if (todo.id === id){
return {
...todo, status: todo.status === 'active' ? 'completed' : 'active'};
} else {
return todo;
}
});
setTodos(newTodos);
}
const clearCompleted = () => {
const updateTodos = todos.map((todo): Todo => {
if(todo.status === 'completed') {
return {
...todo, status: 'cleared'};
} else {
return todo;
}
});
setTodos(updateTodos);
}
const changeFilter = (filter: FilterTypes) => {
setFilter(filter);
}
return (
<div>
<TodoHeader filter={
filter} changeFilter={
changeFilter} addTodo={
addTodo} />
<TodoList todos={
todos} filter={
filter} toggleCompleted={
toggleCompleted} />
<TodoFooter todos={
todos} clearCompleted={
clearCompleted} />
</div>
)
}
src/components/TodoHeader.tsx
如下
import React from 'react';
export const TodoHeader = (props) => {
const [inputText, setInputText] = React.useState<string>('');
const {
filter, changeFilter, addTodo } = props;
const onInput = (e) => {
setInputText(e.target.value);
}
const onSubmit = () => {
if (inputText.length > 0) {
addTodo(inputText);
};
setInputText('');
}
const onFilter = (e) => {
changeFilter(e.currentTarget.textContent);
}
return (
<header>
<h1>todos</h1>
<div className="addTodo">
<input className="textfield" placeholder = "add todo"
value={
inputText} onChange={
onInput} />
<button className="submit" onClick={
onSubmit}>Add</button>
</div>
<nav className="filter">
<button className={
filter === 'all' ? 'selected' : ''} onClick={
onFilter}>all</button>
<button className={
filter === 'active' ? 'selected' : ''} onClick={
onFilter}>active</button>
<button className={
filter === 'completed' ? 'selected' : ''} onClick={
onFilter}>completed</button>
</nav>
</header>
)
}
src/components/TodoList.tsx
如下
import React from 'react';
import {
TodoListItem } from './TodoListItem.tsx';
import {
FilterTypes, Todos } from '../TodoApp.types';
interface TodoListProps {
filter: FilterTypes;
toggleCompleted: (id: string) => void;
todos: Todos;
}
export const TodoList = (props: TodoListProps) => {
const {
todos, filter, toggleCompleted } = props;
const filteredTodos = todos.filter((todo) => {
if (todo.status === 'cleared'){
return false;
};
if (filter === 'all') {
return true;
} else if (filter === 'completed') {
return todo.status === 'completed';
} else if (filter === 'active'){
return todo.status === 'active';
};
return false;
})
return (
<ul className='todos'>
{
filteredTodos.map((todo)=> <TodoListItem key={
todo.id} {
...todo} toggleCompleted={
toggleCompleted} />)}
</ul>
)
}
src/components/TodoListItem.tsx
如下
import React from 'react';
import {
Todo, ToggleCompleted } from '../TodoApp.types';
interface TodoListItemProps extends Todo {
toggleCompleted: ToggleCompleted;
}
export const TodoListItem = (props: TodoListItemProps) => {
const {
label, status, id, toggleCompleted } = props;
const handleCheck = () => {
toggleCompleted(id);
}
return (
<li className="todo">
<label>
<input type="checkbox" checked={
status === 'completed'} onChange={
handleCheck}/> {
label}
</label>
</li>
);
}
2 使用Context来传参
之前的代码,传参的地方比较多,有一些乱。
且传递参数部分比较麻烦。
比如toggleCompleted
这个方法, TodoApp
要先传递给TodoList
,再由TodoList
传递给TodoListItem
。
这样的麻烦在控件嵌套层数一多时,格外明显。
所以这里优化下,使用Context
来传参。
修改后,代码如下
src/TodoApp.types.ts
如下
export type FilterTypes = 'all' | 'active' | 'completed';
export type TodyType = 'active' | 'completed' | 'cleared';
export interface Todo {
id: string;
label: string;
status: TodoType;
}
export type Todos = Todo[];
export type AddTodo = (label: string) => void;
export type ToggleCompleted = (id: string) => void;
export type ClearCompleted = () => void;
export type ChangeFilter = (filter: FilterTypes) => void;
export interface AppContextProps {
addTodo: (label: string) => void;
toggleCompleted: (id: string) => void;
clearCompleted: () => void;
changeFilter: (filter: FilterTypes) => void;
getFilter: () => FilterTypes;
getTodos: () => Todos;
}
src/TodoApp.tsx
如下
import React from 'react';
import {
TodoHeader } from './components/TodoHeader.tsx';
import {
TodoList } from './components/TodoList.tsx';
import {
TodoFooter } from './components/TodoFooter.tsx';
import {
Todo, Todos, FilterTypes, AppContextProps} from './TodoApp.types';
export const AppContext = React.createContext<AppContextProps>(undefined);
const defaultTodos: Todos = [
{
id: '01',
label: 'Todo 1',
status: 'completed',
},
{
id: '02',
label: 'Todo 2',
status: 'active',
},
{
id: '03',
label: 'Todo 2',
status: 'active',
},
{
id: '04',
label: 'Todo 3',
status: 'completed',
},
]
export const TodoApp = () => {
const [filter, setFilter] = React.useState<FilterTypes>('all');
const [todos, setTodos] = React.useState<Todos>(defaultTodos);
const addTodo = (label: string): void => {
const getId = () => Date.now().toString();
const newTodo = {
id: getId(),
label: label,
status: 'active',
};
setTodos([...todos, newTodo]);
}
const toggleCompleted = (id: string) => {
const newTodos = todos.map((todo): Todo =>{
if (todo.id === id){
return {
...todo, status: todo.status === 'active' ? 'completed' : 'active'};
} else {
return todo;
}
});
setTodos(newTodos);
}
const clearCompleted = () => {
const updateTodos = todos.map((todo): Todo => {
if(todo.status === 'completed') {
return {
...todo, status: 'cleared'};
} else {
return todo;
}
});
setTodos(updateTodos);
}
const changeFilter = (filter: FilterTypes) => {
setFilter(filter);
}
const getFilter = () => {
return filter;
}
const getTodos = () => {
return todos;
}
return (
<AppContext.Provider value={
{
addTodo,
toggleCompleted,
clearCompleted,
changeFilter,
getFilter,
getTodos
}}>
<TodoHeader />
<TodoList />
<TodoFooter />
</AppContext.Provider>
)
}
src/components/TodoHeader.tsx
如下
import React, {
ChangeEventHandler, MouseEventHandler, useState, useContext } from 'react';
import {
FilterTypes } from '../TodoApp.types';
import {
AppContext } from '../TodoApp.tsx';
export const TodoHeader = () => {
const [inputText, setInputText] = useState<string>('');
const {
filter, changeFilter, addTodo } = useContext(AppContext);
const onInput: ChangeEventHandler<HTMLInputElement> = (e) => {
setInputText(e.target.value);
}
const onSubmit = () => {
if (inputText.length > 0) {
addTodo(inputText);
};
setInputText('');
}
const onFilter: MouseEventHandler<HTMLButtonElement> = (e) => {
changeFilter(e.currentTarget.textContent as FilterTypes);
}
return (
<header>
<h1>todos</h1>
<div className="addTodo">
<input className="textfield" placeholder = "add todo"
value={
inputText} onChange={
onInput} />
<button className="submit" onClick={
onSubmit}>Add</button>
</div>
<nav className="filter">
<button className={
filter === 'all' ? 'selected' : ''} onClick={
onFilter}>all</button>
<button className={
filter === 'active' ? 'selected' : ''} onClick={
onFilter}>active</button>
<button className={
filter === 'completed' ? 'selected' : ''} onClick={
onFilter}>completed</button>
</nav>
</header>
)
}
src/components/TodoList.tsx
如下
import React from 'react';
import {
TodoListItem } from './TodoListItem.tsx';
import {
AppContext } from '../TodoApp.tsx';
export const TodoList = () => {
const {
getTodos, getFilter } = React.useContext(AppContext);
const filteredTodos = getTodos().filter((todo) => {
if (todo.status === 'cleared'){
return false;
};
if (getFilter() === 'all') {
return true;
} else if (getFilter() === 'completed') {
return todo.status === 'completed';
} else if (getFilter() === 'active'){
return todo.status === 'active';
};
return false;
})
return (
<ul className='todos'>
{
filteredTodos.map((todo)=> <TodoListItem key={
todo.id} {
...todo} />)}
</ul>
)
}
src/components/TodoListItem.tsx
如下
import React from 'react';
import {
Todo } from '../TodoApp.types';
import {
AppContext } from '../TodoApp.tsx';
export const TodoListItem = (props: Todo) => {
const {
label, status, id } = props;
const {
toggleCompleted } = React.useContext(AppContext);
const handleCheck = () => {
toggleCompleted(id);
}
return (
<li className="todo">
<label>
<input type="checkbox" checked={
status === 'completed'} onChange={
handleCheck}/> {
label}
</label>
</li>
);
}
src/components/TodoFooter.tsx
如下
import React from 'react';
import {
AppContext } from '../TodoApp.tsx';
export const TodoFooter = (props) => {
const {
clearCompleted, getTodos } = React.useContext(AppContext);
const itemCount = getTodos().filter((todo) => todo.status === 'active' ).length;
return (
<footer>
<span>{
itemCount} item{
itemCount === 1 ? '' : 's'} left</span>
<button className="submit" onClick={
clearCompleted} >Clear Completed</button>
</footer>
)
}
到这里,TodoApp就优化完成了。
这里页面运行的效果和第四节部分是一样的。
提交并操作pull request合并
最后提交改动到github,并推送(push),或者也可以点击publish按钮
在github上应该会产生一个pull request,点击绿色按钮compare & pull request
进去
然后在跳转到的页面,点击下面的绿色按钮Create pull request
,
再在新跳转出来的页面,点击Merge pull request
, 并确认(点击Confirm merge
)