React 01 :: State & Props

544 阅读4分钟

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:

gitlab.com/yafeya/reac…

首先看一下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

9. 运行效果

screen-capture.gif