创建ts+react的命令
npx create-react-app react-ts --template typescript
使用React和TypeScript进行开发与使用JavaScript进行开发在逻辑上是相同的,但TypeScript增加了类型安全性,这在大型应用中可以减少错误,提高代码的可维护性。下面我将分别用JavaScript和TypeScript演示父子组件的通信。
具体实现
JS实现父子组件的通信
父组件传递数据给子组件
ParentComponent.js
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [parentData, setParentData] = useState('Data from Parent');
return (
<div>
<h1>Parent Component</h1>
<ChildComponent data={parentData} />
</div>
);
}
export default ParentComponent;
ChildComponent.js
import React from 'react';
function ChildComponent(props) {
return (
<div>
<h2>Child Component</h2>
<p>{props.data}</p>
</div>
);
}
export default ChildComponent;
子组件传递数据给父组件
ParentComponent.js
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [childData, setChildData] = useState('');
const handleChildData = (data) => {
setChildData(data);
};
return (
<div>
<h1>Parent Component</h1>
<ChildComponent onData={handleChildData} />
<p>Data from Child: {childData}</p>
</div>
);
}
export default ParentComponent;
ChildComponent.js
import React from 'react';
function ChildComponent(props) {
const sendDataToParent = () => {
props.onData('Data from Child');
};
return (
<div>
<h2>Child Component</h2>
<button onClick={sendDataToParent}>Send Data to Parent</button>
</div>
);
}
export default ChildComponent;
TypeScript实现父子组件的通信
1. 父组件传递数据给子组件
ParentComponent.tsx
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [parentData, setParentData] = useState<string>('Data from Parent');
return (
<div>
<h1>Parent Component</h1>
<ChildComponent data={parentData} />
</div>
);
}
export default ParentComponent;
ChildComponent.tsx
import React from 'react';
interface ChildComponentProps {
data: string;
}
const ChildComponent: React.FC<ChildComponentProps> = ({ data }) => {
return (
<div>
<h2>Child Component</h2>
<p>{data}</p>
</div>
);
};
export default ChildComponent;
ts 相关知识介绍
在TypeScript中,接口(Interface)和类型别名(Type Alias)都是用来定义对象结构的方式,但它们之间有一些关键的区别。下面我将为你详细介绍这两种定义方式,并解释如何在React组件中使用它们来声明Props的类型。
接口(Interface)
接口在TypeScript中是一种定义对象结构的方式,它通常用于定义对象的“形状”(shape)。接口可以包含属性、方法、索引签名等,但不能包含具体的实现。使用接口可以确保某个对象符合特定的结构。
示例:
interface Person {
name: string;
age: number;
greet: () => void;
}
const user: Person = {
name: "Alice",
age: 30,
greet: () => console.log(`Hello, my name is ${this.name}`),
};
在这个例子中,Person
接口定义了一个具有 name
、age
和 greet
方法的对象结构。user
对象实现了这个接口。
类型别名(Type Alias)
类型别名是另一种定义对象结构的方式,但它更加灵活。类型别名可以用来描述基本类型、联合类型、元组、字面量类型等,并且可以包含更复杂的类型定义,如交叉类型、条件类型等。
示例:
type Person = {
name: string;
age: number;
};
type GreetingFunction = () => void;
type User = Person & { greet: GreetingFunction };
const user: User = {
name: "Bob",
age: 25,
greet: () => console.log(`Hello, my name is ${user.name}`),
};
在这个例子中,User
类型别名使用了交叉类型,结合了 Person
对象和 greet
方法。
在React组件中声明Props类型
在React组件中,你可以使用接口或类型别名来定义组件的Props类型,以确保组件接收到的Props符合预期的结构。
使用接口:
interface MyComponentProps {
title: string;
count: number;
}
const MyComponent: React.FC<MyComponentProps> = ({ title, count }) => (
<div>{title} - {count}</div>
);
使用类型别名:
type MyComponentProps = {
title: string;
count: number;
};
const MyComponent: React.FC<MyComponentProps> = ({ title, count }) => (
<div>{title} - {count}</div>
);
在这两个示例中,MyComponent
组件都使用了 React.FC
(Functional Component)来定义,并且通过泛型参数指定了Props的类型。这样,当组件的Props不符合定义的类型时,TypeScript编译器会给出错误提示。
总的来说,接口和类型别名都是TypeScript中定义类型的强大工具,选择使用哪一个取决于你的具体需求和个人偏好。接口更适合用于定义对象的结构,而类型别名提供了更多的灵活性和复杂类型的定义能力。在React组件中,使用它们可以提高代码的可维护性和可读性。
React.FC<MyComponentProps>
React.FC 是TypeScript中的泛型语法。结合了React的函数组件类型 React.FC(Functional Component的缩写)。
这段代码中使用的 React.FC<MyComponentProps>
是TypeScript中的泛型语法,结合了React的函数组件类型 React.FC
(Functional Component的缩写)。让我来详细解释一下这个语法:
函数组件类型(React.FC)
在React中,函数组件是一个返回React元素的函数。React 16.8及以上版本引入了Hooks,使得函数组件可以包含状态和副作用。React.FC
是React TypeScript定义中用于类型化函数组件的泛型类型。
泛型(Generics)
泛型是TypeScript的一个特性,它允许你定义函数、类、接口或类型别名,这些可以接收一个或多个类型参数,然后在定义中使用这些类型参数来创建一个可重用的组件,这个组件可以用于多种不同的类型。
React.FC
当你看到 React.FC<MyComponentProps>
这样的写法时,实际上是在告诉TypeScript编译器:
React.FC
是一个泛型类型,它需要一个类型参数来指定组件的Props类型。<MyComponentProps>
是传递给React.FC
的类型参数,它是一个接口,定义了组件的Props结构。
这样,MyComponent
组件就被类型化为一个函数组件,它的Props必须符合 MyComponentProps
接口中定义的结构。如果传递给组件的Props不符合这个结构,TypeScript编译器将会给出错误。
示例解释
在你提供的代码示例中:
interface MyComponentProps {
title: string;
count: number;
}
const MyComponent: React.FC<MyComponentProps> = ({ title, count }) => (
<div>{title} - {count}</div>
);
MyComponentProps
接口定义了组件的Props,包括一个title
属性(类型为string
)和一个count
属性(类型为number
)。MyComponent
组件使用React.FC<MyComponentProps>
来声明它的类型,这意味着它是一个函数组件,其Props类型由MyComponentProps
接口定义。- 当你调用
MyComponent
组件并传递Props时,这些Props需要符合MyComponentProps
接口的结构,否则TypeScript编译器会报错。
这种写法的好处是提高了代码的类型安全性和可维护性,因为TypeScript编译器会在编译时检查Props的类型,帮助开发者避免运行时错误。
2. 子组件传递数据给父组件
ParentComponent.tsx
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [childData, setChildData] = useState<string>('');
const handleChildData = (data: string) => { // 可以直接将set函数作为回调函数传递给子组件,而不需要额外封装一个函数
setChildData(data);
};
return (
<div>
<h1>Parent Component</h1>
<ChildComponent onData={handleChildData} />
<p>Data from Child: {childData}</p>
</div>
);
}
export default ParentComponent;
ChildComponent.tsx
import React from 'react';
interface ChildComponentProps {
onData: (data: string) => void;
}
const ChildComponent: React.FC<ChildComponentProps> = ({ onData }) => {
const sendDataToParent = () => { // 可以直接调用onData函数,不用再封装一次
onData('Data from Child');
};
return (
<div>
<h2>Child Component</h2>
<button onClick={sendDataToParent}>Send Data to Parent</button>
</div>
);
};
export default ChildComponent;
区别总结
-
类型声明:
- 在JavaScript中,没有类型声明,所有类型都是隐式的。
- 在TypeScript中,使用接口(interface)或者类型别名(type)来定义Props的类型,并在组件中明确声明这些类型。
-
代码的安全性:
- TypeScript通过静态类型检查,在编译时捕捉类型错误,增强了代码的安全性和可维护性。
- JavaScript没有静态类型检查,类型错误只能在运行时发现。
-
开发体验:
- 使用TypeScript,IDE(如VSCode)会提供更好的代码补全、类型提示和重构支持。
通过这两种实现方式的对比,你可以看到TypeScript在类型安全性和代码可维护性方面的优势,尤其是对于大型项目,这些优势会更加明显。
完整代码
Father.tsx
import { useState } from "react";
import Son from "./Son";
const Father = () => {
// 注意这里和js不同,要加<number>,当然,并不是必须的,删了也没有报错
// <number> 是TypeScript的泛型语法,表示我们希望 useState 管理的状态变量的类型是 number
const [fatherMoney, setFatherMoney] = useState<number>(100);
return (
<div style={{ border: "blue 1px solid" }}>
<p>这里是父亲组件,fatherMoney:{fatherMoney}</p>
<Son data={fatherMoney} changeFatherMoney={setFatherMoney}></Son>
// 这里可以直接把set函数传递过去,不用在封装一个函数
</div>
);
};
export default Father;
Son.tsx
import React from "react";
interface SonProps {
data: number; // 父传子
changeFatherMoney: (data: number) => void; // 子传父,需要在接口,里面定义类型,不然Father回报错,就是在添加onClick={() => changeFatherMoney(data - 1)}的时候
}
const Son: React.FC<SonProps> = ({ data, changeFatherMoney }) => {
return (
<div style={{ border: "1px red solid" }}>
<p>这里是子组件</p>
父亲的数据是:{data}
// 这里可以直接传参数
<button onClick={() => changeFatherMoney(data - 1)}>用父亲一块钱</button>
</div>
);
};
export default Son;
App.tsx
import Father from "./compoents/Father";
function App() {
return (
<div className="App">
<Father></Father>
</div>
);
}
export default App;
React.FC<ChildComponentProps>
解释
在TypeScript中,React.FC<ChildComponentProps>
是用于定义 React 函数组件的一种类型注解。它有助于确保组件的属性(props)符合预期的类型,并为组件的使用提供类型安全和智能提示。
详细解释
React.FC
是React.FunctionComponent
的简写,是一个泛型接口,用于定义函数组件。ChildComponentProps
是一个接口或类型别名,用于描述组件的 props 的结构和类型。
通过使用 React.FC<ChildComponentProps>
,我们告诉 TypeScript 这个函数组件将接收的 props 必须符合 ChildComponentProps
接口的定义。
示例代码
定义 ChildComponentProps
接口
首先,我们定义一个接口 ChildComponentProps
,描述这个组件所需要的 props 的类型:
interface ChildComponentProps {
data: string; // props 中需要有一个字符串类型的 `data`
}
使用 React.FC<ChildComponentProps>
然后,我们定义一个函数组件 ChildComponent
,并使用 React.FC<ChildComponentProps>
进行类型注解:
import React from 'react';
interface ChildComponentProps {
data: string;
}
const ChildComponent: React.FC<ChildComponentProps> = ({ data }) => {
return (
<div>
<h2>Child Component</h2>
<p>{data}</p>
</div>
);
};
export default ChildComponent;
主要优势
-
类型安全:
- 确保组件接收的 props 符合预期的类型。在编写或使用组件时,如果提供的 props 类型不正确,TypeScript 会在编译时提示错误。
-
自动推断和提示:
- 使用
React.FC
,TypeScript 会自动推断组件的返回类型为JSX.Element
,并为 props 提供智能提示和自动补全。
- 使用
-
默认包含
children
:React.FC
默认包含了children
属性,这对于需要传递子元素的组件非常方便。如果不需要children
属性,可以显式地将其从接口中移除。
包含 children
属性
import React from 'react';
interface ChildComponentProps {
data: string;
}
const ChildComponent: React.FC<ChildComponentProps> = ({ data, children }) => {
return (
<div>
<h2>Child Component</h2>
<p>{data}</p>
{children}
</div>
);
};
export default ChildComponent;
不包含 children
属性
如果你不希望组件接受 children
属性,可以将其显式移除:
import React, { ReactNode } from 'react';
interface ChildComponentProps {
data: string;
children?: never; // 显式移除 `children` 属性
}
const ChildComponent: React.FC<ChildComponentProps> = ({ data }) => {
return (
<div>
<h2>Child Component</h2>
<p>{data}</p>
</div>
);
};
export default ChildComponent;
总结
通过使用 React.FC<ChildComponentProps>
进行类型注解,你可以确保组件的 props 类型安全,并享受更好的开发体验,包括智能提示和自动补全。 这种类型注解在团队合作和大型项目中尤其重要,因为它可以显著减少由于类型错误引起的 bug。