使用TypeScript版本的React实现TodoList应用

使用TypeScript版本的React实现TodoList应用

新建项目

  1. 确保你安装了较新版本的Node.js

  2. 创建一个新的项目

    npx create-react-app todo-react-ts --template typescript
    
  3. 删除多余的文件

    cd todo-react-ts
    rm src/*
    
  4. 新建下列文件

    touch src/index.tsx src/App.tsx
    

    并分别填入下面的内容:

    // index.tsx
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    ReactDOM.render(
      <App />,
      document.getElementById('root')
    );
    
    // App.tsx
    import React from 'react';
    const App: React.FC = () => {
        return <div>Hello World!</div>
    };
    export default App;
    
  5. 执行npm start,在浏览器中如下图所示则表示环境准备成功。

在这里插入图片描述

组件组成

在这里插入图片描述

应用组件结构如下:

App
--TodoList
---- TodoListItem
--AddTodoForm

实现TodoListItem组件

  1. src下新建一个TodoListItem.tsx文件,一个types.ts文件,一个TodoListItem.css文件

    touch src/TodoListItem.tsx
    touch src/types.ts
    touch	src/TodoListItem.css
    
  2. 写入下列的内容

    // TodoListItem.tsx
    
    import React from 'react';
    import { Todo } from './types'
    import './App.css';
    
    interface TodoListItemProps {
        todo: Todo
    }
    
    export const TodoListItem: React.FC<TodoListItemProps> = ({ todo }) => {
        return (
            <li>
                <label className={todo.complete ? "complete" : undefined}>
                    <input type="checkbox" checked={todo.complete} />
                    {todo.text}
                </label>
            </li>
        );
    }
    
    
    // types.ts
    
    export type Todo = {
        text: string;
        complete: boolean;
    }
    
    // TodoListItem.css
    .complete {
        text-decoration: line-through;
    }
    

    刚刚我们定义一个函数组件TodoListItem, 从interface可以看出,它接受一个Todo类型的属性,Todo有两个字段,分别是text用于展示文本内容,complete用来表示是否完成,它控制label样式和checkbox的状态。

  3. App.tsx中引用我们定义的TodoListItem组件

    import React from 'react';
    import { TodoListItem } from './TodoListItem';
    import { Todo } from './types'
    
    const todos: Todo[] = [
        {text: "Javascript", complete: true},
        {text: "Typescript", complete: false}
    ]; 
    
    const App: React.FC = () => {
        return (
            <div>
                <TodoListItem  todo = {todos[0]}/>
                <TodoListItem  todo = {todos[1]}/>
            </div>
        );
    };
    
    export default App;
    
  4. 最终效果如下:

    image-20200425161101084

实现TodoList组件

  1. 新建TodoList.tsx

    touch src/TodoList.tsx
    
  2. TodoList.tsx写入下列内容

    import React from 'react';
    import { TodoListItem } from './TodoListItem';
    import { Todo, ToggleTodo } from './types'
    
    interface TodoListProps {
        todos: Todo[],
        toggleTodo: ToggleTodo // type ToggleTodo = (selectedTodo: Todo) => void
    };
    
    export const TodoList: React.FC<TodoListProps> = ({ todos, toggleTodo }) => {
        return (
            <ul>
                {todos.map(todo => {
                    return <TodoListItem key={todo.text} todo={todo} toggleTodo={toggleTodo} />
                })}
            </ul>
        );
    };
    
    

    todostoggleTodo是由父组件传递的属性,其中todos是代办数组,而toggleTodo则是改变todo状态的函数,由父组件定义

  3. App.tsx修改为下列内容

    import React, { useState } from 'react';
    import { Todo, ToggleTodo } from './types'
    import { TodoList } from './TodoList';
    
    const initialTodos: Todo[] = [
        {text: "Javascript", complete: true},
        {text: "Typescript", complete: false}
    ]; 
    
    const App: React.FC = () => {
        const [todos, setTodos] = useState(initialTodos);
        const toggleTodo: ToggleTodo = selectedTodo => {
            const newTodos = todos.map(todo => {
                if (todo === selectedTodo) { // 被选择的todo更新其状态
                    return {
                        ...todo,
                        complete: !todo.complete
                    };
                }
                return todo; // 其他todo直接返回
            });
            setTodos(newTodos); // 将todos更新为newTodos
        };
        return (
            <div>
                <TodoList todos={todos} toggleTodo={toggleTodo} />
            </div>
        );
    };
    
    export default App;
    
    1. 我们使用useState来为函数组件App引入一个状态todos ,该状态用来保存所有的todo,然后赋予其一个初始值initialTodos
    2. 定义了一个todo状态改变函数toggleTodo,入参是被选择的todo,处理逻辑是将该todo状态改变,即反转其complete属性,其余保持不变,并使用setTodos更新todos
    3. 引用我们刚刚定义的TodoList组件,并传递todostoggleTodo属性
  4. 最终效果如下

在这里插入图片描述

实现AddTodoForm组件

  1. 新建AddTodoForm.tsx文件

    touch src/AddTodoForm.tsx
    
  2. 写入下列内容

    import React, { useState, ChangeEvent, FormEvent } from 'react';
    import { AddTodo } from './types';
    
    interface AddTodoFormProps {
        addTodo: AddTodo  // type AddToto = (newTodo: string) => void
    };
    
    export const AddTodoForm: React.FC<AddTodoFormProps> = ({ addTodo }) => {
        const [newTodo, setNewTodo] = useState('');
        const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
            setNewTodo(e.target.value);
        };
        const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
            e.preventDefault();
            addTodo(newTodo);
            setNewTodo('');
        };
        return (
            <form onSubmit={handleSubmit}>
                <input type="text" value={newTodo} onChange={handleChange} />
                <button type="submit">Add Todo</button>
            </form>
        );
    };
    
    
    1. 使用useState为函数组件引入一个状态newTodo,用来保存当前用户输入的新todo
    2. <input/>value赋为newTodo, 并监听change事件,每次改变时调用handleChangenewTodo置为新输入值,实现双向绑定
    3. 当点击按钮,即submit时,首先使用e.preventDefault()阻止事件的默认行为,然后调用父组件传过来的addTodo函数(这是因为todos状态都在父组件中,所以修改、增加都必须使用父组件传过来的函数),将当前newTodo加入到所有todo中,最后调用setNewTodonewTodo置空
  3. 修改App.tsx为下列内容

    import React, { useState } from 'react';
    import { Todo, ToggleTodo, AddTodo } from './types'
    import { TodoList } from './TodoList';
    import { AddTodoForm } from './AddTodoForm';
    
    const initialTodos: Todo[] = [
        {text: "Javascript", complete: true},
        {text: "Typescript", complete: false}
    ]; 
    
    const App: React.FC = () => {
        const [todos, setTodos] = useState(initialTodos);
        const toggleTodo: ToggleTodo = selectedTodo => {
            const newTodos = todos.map(todo => {
                if (todo === selectedTodo) {
                    return {
                        ...todo,
                        complete: !todo.complete
                    };
                }
                return todo;
            });
            setTodos(newTodos);
        };
        const addTodo: AddTodo = newTodo => {
            if (newTodo.trim() === '')  // 空值,则跳过
                return;
            setTodos([	// 更新todos
                ...todos,
                {
                    text: newTodo,
                    complete: false
                }
            ]);
        };
        return (
            <div>
                <TodoList todos={todos} toggleTodo={toggleTodo} />
                <AddTodoForm addTodo={addTodo} />
            </div>
        );
    };
    
    export default App;
    
  4. 最终效果如下

    addtodoform-demo

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值