React从0开始(二):组件

284 阅读4分钟

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

上一篇文章写了ReactJSX写法,也清楚了JSX是怎样的一回事、React.createElementReact Element是怎样的一个关系,今天来了解一下React的组件

一、组件的定义

在上一篇文章里有讲到React项目的目录结构,为了更简单地开始学习React项目,我仅在src目录内保留React的入口文件index.js,剩下的东西从头开始写

image.png

Hello React开始

  1. src目录下创建Hello.js注意:文件有个命名规则,首字母必须为大写

  2. 打开Hello.js,使用JSX进行编写并导出,使用JSX必须先导入react

import React from 'react';

//定义组件,继承 React.Component
class Hello extends React.Component {
    //构造函数
    constructor(props){
        super(props);
        this.state = {};
    }
    //render函数,内部编写并返回JSX模板
    render() {
        return (
            <h1>Hello React</h1>
        )
    }
}
//导出组件对象
export default Hello

到这里,一个简单的Hello组件就写好了,基本流程如下:

image.png

graph TD
引入react库 --> 定义组件类并继承React.Component --> 构造函数初始化数据 --> render函数返回JSX模板 --> 利用export导出

二、组件的引用

写完了Hello组件,接下来当然就是要使用它啦!

题外话:对于真实DOM的渲染,需要引用两个库,一个是react用于JSX的编写,另一个则是react-dom用于虚拟DOM真实DOM之间的变化(利用render方法,有ReactElementContainer[callback]三个参数,具体可看上一篇文章)

在这里我们将Hello组件挂载到public/index.htmlIDroot的元素下:

index.js 中使用 Hello

import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './Hello';
//进行渲染
ReactDOM.render(
    <Hello />,
    document.getElementById('root')
)

运行后我们访问localhost:3000,可以看到Hello组件已经被成功使用

image.png

三、组件内的方法与表达式

在组件内使用变量、相应的方法是必不可少的,这里通过一个列表的例子来进行说明。

JSX中的表达式

JSX内使用表达式,只需要用花括号{}括住即可(如{true?"one":"zero"}

组件的状态State

组件的状态是可变的,可以通过State对组件的状态更改进行改变与呈现,可通过this.setState({key:value})对状态进行更改,且这一操作并非是覆盖更改,而是定向更改(即仅对参数对象中的对象内容进行定向修改)

组件的props

props总是单项地父组件流向子组件props是只读的、纯净不可变的,可通过父组件修改props来触发子组件更新

Stateprops区别

条件StateProps
从父组件中接收初始值TrueTrue
父组件可以改变值FalseTrue
在组件中设置默认值TrueTrue
在组件的内部变化TrueFalse
设置子组件的初始值TrueTrue
在子组件的内部更改FalseTrue

内嵌表达式的JSX以及组件内数据与方法

  1. 组件的创建与初始化:创建List组件,并定义初始化状态数据,通过内嵌表达式的JSX语法进行组件编写
import React from 'react';

class List extends React.Component {
    constructor(props){
        super(props);
        //初始化状态
        this.state = {
            techList:["Javascript","PHP","Java"]
        };
    }
    render() {
        return (
            <ul id="list">
                {
                    this.state.techList.map((item,index)=>{
                        return <li key={index+item} >{index}----{item}</li>
                    })
                }
            </ul>
        )
    }
}

export default List

image.png

  1. 组件状态的修改:通过this.setState修改状态,通过自定义方法添加内容到列表中

  2. 父子组件的传参

对上方的List组件做个拆分,写一个名为ListItem的子组件,子组件同样有有keyindexitem三个参数

另外,props只能是由父组件到子组件的单项数据流,且不能被子组件修改(只读),子组件可通过父组件传入的方法去修改父组件的状态

//List.js
render() {
    return (
        <ul id="list">
            {
                this.state.techList.map((item,index)=>{
                    return <ListItem key={index + item} item={item} index={index} />
                })
            }
        </ul>
    )
}
//ListItem.js
render() {
    return (
        <li>{this.props.index}----{this.props.item}</li>
    )
}
  1. 点击事件以及列表项的删除方法

这一块我们会了解到如何定义组件的事件、子组件通过props对父组件进行状态的修改。我们先在List.js上做文章,写好删除的方法并做好事件的绑定

这里提出一个网上看到的推荐:建议将方法写在父组件上传入子组件,通过子组件去改变父组件的状态

//List.js
render() {
    return (
        <ul id="list">
            {
                this.state.map((item,index)=>{
                    return <ListItem deleteItem={this.deleteItem.bind(this,index)} key={index+item} index={index} item={item} />
                })
            }
        </ul>
    )
}
deleteItem(index) {
    let list = this.state.techList;
    list.splice(index,1);
    this.setState({
        techList:list
    })
}
//ListItem.js
<li onClick={this.props.deleteItem} >{this.props.index}----{this.props.item}</li>

另外提一句:在绑定事件时,是需要对方法与当前this进行一次绑定的,否则this会是undefined