1. React Component的实现方式
React的Component有两种实现方式,Class和Functional,早期版本,Class的Component功能更为强大,可以实现状态管理与生命周期hooks的管理,不过在现在版本的React中,这些功能在Functional的Component中都可以实现。
状态管理可以通过useState
实现。
生命周期的hooks可以通过useEffect
来实现。
而且使用Functional的实现方式,可以节省一些代码量,类似于构造函数可以省略,不过根据大家应用场景不同,可以自行选择喜欢的方式实现Component。
回到正文,这篇文章主要给大家share一下如何在React+Typescript的场景下使用Props和State。这篇文章是本系列的第二篇文章,本系列主要是关注与React的基础用法,给新手入门使用。
2. Props
Props主要用来在Component之间传递数据,不单单可以传递只类型数据,也可以传递函数。后面讲通过实例的方式为大家分别演示如何在Class与Functional的Component中使用Props。
3. State
State用于控制Component的状态,state的改变可以重新调用component的render
方法。也就是由于这个原因,虽然state和props都可以做页面的数据展示,但是,如果需要有数据的交互,那就必须使用state。
需要注意的是,在class component中,为页面设置初始化state必须直接set state,在页面加载后设置state必须调用React的Api——setState
,后面也会有专门的例子来说明这一点。
4. Demo整体结构
本示例代码请参照repo:
首先看一下App.tsx
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.scss';
import { Paragraph } from './Paragraph';
import { Image } from './Image';
import { HyperLink } from './HyperLink';
import { Like } from './Like';
import { Clock } from './Clock';
export class App extends Component {
render(){
return (
<div className="App">
<header className="App-header">
<Image src={logo} alt="logo"></Image>
<Paragraph part1="Edit" code="src/App.tsx" part2="and save to reload."></Paragraph>
<HyperLink href="https://reactjs.org" title="Learn React"></HyperLink>
<Like count={0} onLike={count=>console.log(count)}></Like>
<Clock></Clock>
</header>
</div>
);
}
}
复制代码
这里定义了5个Component,Image
, Paragraph
, HyperLink
, Like
, 和 Clock
。我将分别用他们来讲解后续的各种写法。
5. Class Component 使用 Props
// Paragraph.tsx
import { Component } from "react";
export interface ParagraphProps {
part1: string;
part2: string;
code: string;
}
export class Paragraph extends Component<ParagraphProps, {}>{
constructor(props: ParagraphProps){
super(props);
}
render(){
return (
<p>
{this.props.part1} <code>{this.props.code}</code> {this.props.part2}
</p>
)
}
}
复制代码
// App.tsx
// ...
<Paragraph part1="Edit" code="src/App.tsx" part2="and save to reload."></Paragraph>
// ...
复制代码
Paragraph
是使用Class方式编写的Component,他的构造函数必须接收一个强类型的props,用于接收参数。Paragraph
component从App
component接收了3个参数,并直接调用this.props.part1
, this.props.part2
, 和this.props.code
显示在了ux上,这个用法是非常简单的。
6. Functional Component 使用 Props
// Image.tsx
import React from 'react';
import './Image.scss';
export interface ImageProps{
src: string;
alt: string;
}
export const Image: React.FC<ImageProps> = (props)=>{
return (<img src={props.src} className="App-logo" alt={props.alt} />);
};
复制代码
// App.tsx
// ...
<Image src={logo} alt="logo"></Image>
// ...
复制代码
Image
是使用Functional方式编写的Component,也需要定义强类型的Props,用来接收参数,这一点是必不可少的,但是显而易见的是,他的代码要比class方式的少了不少,主要是因为省略了constructor。
值得注意的一点是,在App
中调用时,如果要给component传递的参数是个变量的话需要使用{}
来表示,这是React的基础语法。
7. Class Component 使用 State
import { Component } from "react";
export interface LikeProps{
count: number;
onLike(count: number): void;
}
export interface LikeState{
count: number;
}
export class Like extends Component<LikeProps, LikeState> {
constructor(props: LikeProps){
super(props);
this.state = {
count: props.count
};
}
setCount(count: number){
this.setState({
count: count
});
}
onClick(){
let count = this.state.count+1;
this.setCount(count);
this.props.onLike(count);
}
render(){
return (
<div>
<p>There are {this.state.count} person like you!!</p>
<button className="btn btn-primary" onClick={()=>this.onClick()}>Like</button>
</div>
);
}
}
复制代码
// App.tsx
// ...
<Like count={0} onLike={count=>console.log(count)}></Like>
// ...
复制代码
这个component相对来说有些复杂,因为它既接收了props又使用了state。
- 为state设置初始值
// 构造函数中,必须这样调用,否则会导致页面持续刷新 this.state = { count: props.count }; 复制代码
- 在按钮的响应函数中修改state
setCount(count: number){
// 在页面加载完成后,只能通过setState api对state进行修改
this.setState({
count: count
});
}
onClick(){
let count = this.state.count+1;
this.setCount(count);
this.props.onLike(count);
}
复制代码
- 在按钮的响应函数中调用了props传递的函数
在本例中,onClick()
中直接调用了this.props.onLike(count)
, 这样会使App
中设置的handler被处罚,在console中打印count
。
// App.tsx
// ...
<Like count={0} onLike={count=>console.log(count)}></Like>
// ...
复制代码
8. Functional Component 使用 State
import React, { Component, useEffect, useRef, useState } from "react";
export interface ClockState{
time: Date;
}
export const Clock: React.FC = ()=>{
const [time, setTime] = useState(new Date());
const clockRef = useRef<HTMLParagraphElement>(null);
useEffect(()=>{
setInterval(() => setTime(new Date()), 1000);
}, [clockRef]);
return <p ref={clockRef}>The current time is {time.toLocaleTimeString()}</p>;
};
复制代码
- 在functional component中使用
useState
方法来初始化state,并生成改变state的handler// time的类型将隐式被生命为useState的参数类型 // setTime是改变state中time的handler // 每次调用setTime后,time都会改变。 const [time, setTime] = useState(new Date()); 复制代码
- 在functional component中使用
useEffect
方法来实现componentDidMount
的handler
注:useEffect会接受一个数组,作为dependencies,作用当接收的参数被mount的时候,调用callback
useEffect(()=>{ setInterval(() => setTime(new Date()), 1000); }, [clockRef]); 复制代码
- 在functional component中使用
useRef
方法来获取一个htmlElement