【Typescript学习】使用 React 和 TypeScript 构建web应用(二)部分UI、useState、useRef、Props
教程来自freecodeCamp:【英字】使用 React 和 TypeScript 构建应用程序
跟做,仅记录用
其他资料:https://www.freecodecamp.org/chinese/news/learn-typescript-beginners-guide/
第二天
以下是视频(0:18-0:40) 的内容
目录
- 第二天
- 1 App 函数组件的类型
- 2 头部及其 UI
- 3 Todo的input框 UI
- 4 useState Hook
- 5 函数作为props传递
- 6 useRef Hook
1 App 函数组件的类型
是React.FC
const App: React.FC = () => {//
}
2 头部及其 UI
先做个头部,效果如下
App.tsx
import React from 'react';
import './App.css';
const App: React.FC = () => {return (<div className="App"><span className='heading'>Taskify</span></div>);
}
想引入一个叫neucha的谷歌字体,搜索一下
进入后
复制import信息到css文件
在App.css文件中引入并使用
@import url('https://fonts.googleapis.com/css2?family=Neucha&display=swap');.App {width: 100vw;height: 100vh;display: flex;flex-direction: column;align-items: center;background-color: #2f74c0;font-family: 'Neucha', cursive; /* 使用字体 */
}.heading {text-transform: uppercase;font-size: 40px;color: white;margin: 30px 0;text-align: center;z-index: 1; /* 保持最顶层,为了之后的动画效果 *//* 窗口适应 */
@media (max-width: 800px) { .heading {margin: 15px 0;font-size: 35px;}
}
}
3 Todo的input框 UI
做一下input框
可以安装个扩展插件使用模板快捷键,比如在组件.tsx文件中,空白时输入tsrafce
快捷填入模板
新建一个InputField组件
InputField.tsx
import React from 'react'
import "./styles.css";
type Props = {}const InputField = (props: Props) => {return (<form className='input'><input type="input" placeholder="Enter a task" className='input__box'/><button className='input_submit' type='submit'>Go</button></form>)
}export default InputField
CSS类名都遵循 BEM 命名格式
style.css
.input {display: flex;width: 90%;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;
}
4 useState Hook
我们需要实时地拿到用户输入到input的文字(一条todo),所以可以使用state
,state
用来表示和控制组件中的变量的状态。
为了增删改查,todo列表数据应该是放在App中的,但是新建的每一个todo的string内容在子组件InputField里。因此使用prop进行组件间通信,
我们需要使用prop来在App组件和InputField组件中传递状态变量
- 父(App)向子(InputField)传递传递prop:
todo={todo} setTodo={setTodo}
App.tsx
const App: React.FC = () => {const [todo, setTodo] = useState<string>(""); // 尖括号加上变量类型return (<div className="App"><span className='heading'>Taskify</span><InputField todo={todo} setTodo={setTodo}/></div>);
}
- InputField 中用定义Props type,接收所有传来的prop
setTodo
的类型在App.tsx中悬浮一下就可得到,是React.Dispatch<React.SetStateAction<string>>
InputField.tsx
type Props = {todo: string,setTodo: React.Dispatch<React.SetStateAction<string>>,
}
- 在InputField的元素中使用
todo
和setTodo
const InputField: React.FC<Props> = ({todo, setTodo}: Props) => {return (<form className='input'><input type="input"value={todo} onChange={(e)=>setTodo(e.target.value)}placeholder="Enter a task" className='input__box'/><button className='input_submit' type='submit'>Go</button></form>)
}
Todo列表
因为许多地方复用,所以创建一个model.ts,里面定义interface Todo
export interface Todo {id: number;todo: string; // 内容isDone: boolean; // 是否完成
}
在App.tsx中定义Todo为元素的数组todos
,代表Todo列表
const [todos, setTodos] = useState<Todo[]>([]);
5 函数作为props传递
写handleAdd
函数(填完todo后点击GO时的回调)的逻辑,
// 点击GO后const handleAdd = (e: React.FormEvent):void => {e.preventDefault(); // 取消默认的页面刷新行为if(todo) {// 在列表尾部加一条Todo,时间作为idsetTodos([...todos, {id: Date.now(), todo: todo, isDone: false}])setTodo(""); // 清空input框// 在App里setTodo("")是因为将父组件的state(todo)作为子组件的props// 当父组件的state改变,子组件的props也跟着改变。}};
将函数作为props传递
App.tsx
const App: React.FC = () => {
const [todo, setTodo] = useState<string>("");
const [todos, setTodos] = useState<Todo[]>([]);
// 点击GO后const handleAdd = (e: React.FormEvent):void => {e.preventDefault(); // 取消默认的页面刷新行为if(todo) {// 在列表尾部加一条Todo,时间作为idsetTodos([...todos, {id: Date.now(), todo: todo, isDone: false}])setTodo(""); // 清空input框// 在App里setTodo(""),因为将父组件的state(todo)作为子组件的props// 当父组件的state改变,子组件的props也跟着改变。}};return (<div className="App"><span className='heading'>Taskify</span><InputField todo={todo} setTodo={setTodo} handleAdd={handleAdd}/></div>);}
在InputField.tsx中的Props
定义里加上handleAdd
,形参类型为React.FormEvent
,若类型不清楚可以google到的
type Props = {todo: string,setTodo: React.Dispatch<React.SetStateAction<string>>,handleAdd: (e: React.FormEvent) => void,
}const InputField: React.FC<Props> = ({todo, setTodo, handleAdd}: Props) => {return (<form className='input' onSubmit={handleAdd}>...</form>)
}export default InputField
6 useRef Hook
我们发现回车后input的focus还没有解除,即样式上input周围都是暗色的,这不符合需求。
可以在InputField.tsx中用useRef
来控制,useRef
的作用之一是用于获取DOM元素
- 先通过
useRef
创建一个变量,类型是HTMLInputElement
const inputRef = useRef<HTMLInputElement>(null)
- 然后在jsx中通过
ref={inputRef}
给对应元素节点添加属性
<input type="input"ref={inputRef}value={todo} onChange={(e)=>setTodo(e.target.value)}placeholder="Enter a task" className='input__box'/>
- 在页面挂载后通过
inputRef.current
就可以获取对应节点的真实DOM元素了
<form className='input' onSubmit={(e) => {handleAdd(e);console.log(e);inputRef.current?.blur(); // 移除focus状态// 如果这里报错可以外加一个if先对inputRef.current判非空}}>
第二天 done!
我们构建了InputField组件,完善了UI,复习了useState、useRef、props,截止今天,代码如下:
InputField.tsx
import React, {useRef} from 'react'
import "./styles.css";
type Props = {todo: string,setTodo: React.Dispatch<React.SetStateAction<string>>,handleAdd: (e: React.FormEvent) => void,
}const InputField: React.FC<Props> = ({todo, setTodo, handleAdd}: Props) => {const inputRef = useRef<HTMLInputElement>(null)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>)
}export default InputField
App.tsx
import React, { useState } from 'react';
import './App.css';
import InputField from './components/InputField';
import { Todo } from "./model";const App: React.FC = () => {const [todo, setTodo] = useState<string>(""); // 尖括号加上变量类型const [todos, setTodos] = useState<Todo[]>([]) // 点击GO后const handleAdd = (e: React.FormEvent):void => {e.preventDefault(); // 取消默认的页面刷新行为if(todo) {setTodos([...todos, {id: Date.now(), todo: todo, isDone: false}])setTodo("");}}; return (<div className="App"><span className='heading'>Taskify</span><InputField todo={todo} setTodo={setTodo} handleAdd={handleAdd}/></div>);
}export default App;
心得体会:
React 一段时间不用就容易忘,几种Hook需要捋一捋
使用到的:
- neucha font
- 一种CSS命名规范——BEM
- VSCode 代码模板插件 “ES7 + React/Redux/React-Native snippets”
- useState
- useRef
- props