本文为书籍《深入浅出React和Redux》的笔记 书中React的版本为v15.4,学习时React的版本为v17.0.2 文中提到的ControlPanel.js为父组件,Counter.js为子组件
书中提到的代码下载来源
作为一个合格的开发者,不要只满足于编写出了可以运行的代码,而要了解代码背后的工作原理;不要只满足于自己编写的程序能够运行,还要让自己的代码可读而且易于维护。这样才能开发出高质量的软件。
易于维护组件的设计要素
高内聚:指的是把逻辑紧密相关的内容放在一个组件中。 低耦合:指的是不同组件之间的依赖关系要尽量弱化,也就是每个组件要尽量独立。
React组件的数据分为两种
prop和state,无论prop或者state的改变,都可能引发组件的重新渲染,prop是组件的对外接口,state是组件的内部状态,对外用prop,内部用state。
【解决BUG】 在尝试第二个案例的时候报错了
ERROR in ./src/Counter.js 97:11-38
export 'PropTypes' (imported as 'PropTypes') was not found in
'react' (possible exports: Children, Component, Fragment,
Profiler, PureComponent, StrictMode, Suspense,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
cloneElement, createContext, createElement, createFactory,
createRef, forwardRef, isValidElement, lazy, memo, useCallback,
useContext, useDebugValue, useEffect, useImperativeHandle,
useLayoutEffect, useMemo, useReducer, useRef, useState,
version)
搞了半天,最后还是通过搜索解决了,应该是代码太旧的问题
import React, { Component, PropTypes } from 'react';
把以上代码改为
import React, { Component } from 'react';
import PropTypes from 'prop-types';
【注意】 提前 npm install --save prop-types 此外还要检查其它文件是否有类似问题,都要改
解决链接是:stackoverflow.com/questions/4…
React的prop
在React中,prop(property的简写)是从外部传递给组件的数据,一个React组件通过定义自己能够接受的prop就定义了自己的对外公共接口。
每个React组件都是独立存在的模块,组件之外的一切都是外部世界,外部世界就是通过prop来和组件对话的。
React组件的prop所能支持的类型则丰富得多,除了字符串,可以是任何一种JavaScript语言支持的数据类型。
当prop的类型不是字符串类型时,在JSX中必须用花括号{}把prop值包住,所以style的值有两层花括号,外层花括号代表是JSX的语法,内层的花括号代表这是一个对象常量。如下
给prop(property)赋值
【注意】 React要求render函数只能返回一个元素。
<SampleButton
id="sample"
borderWidth={2} onClick={onButtonClick} style={{color: "red"}}/>
部分示例代码:父组件ControlPanel用prop传递信息给Counter子组件
class ControlPanel extends Component {
render() {
return (
<div>
<Counter caption="First" initValue={0} />
<Counter caption="Second" initValue={10} />
<Counter caption="Third" initValue={20} />
</div>
//通过名为caption的prop,ControlPanel传递给Counter组件实例说明文字。
//通过名为initValue的prop传递给Counter组件一个初始的计数值。
);
}}
读取prop值
来看Counter组件内部是如何接收传入的prop的
【名词解释】构造函数:是一种特殊的方法。 主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
首先是构造函数,代码如下:
class Counter extends Component {
constructor(props) {
super(props);
如果一个组件需要定义自己的构造函数,
一定要记得在构造函数的第一行通过super调用父类也
就是React.Component的构造函数。
this.onClickIncrementButton =
this.onClickIncrementButton.bind(this);
this.onClickDecrementButton =
this.onClickDecrementButton.bind(this);
this.state = {
count: props.initValue || 0
// 在构造函数中可以通过参数props获得传入prop值
// 在其他函数中则可以通过this.props访问传入prop的值,
}
}
propTypes检查
既然prop是组件的对外接口,那么就应该有某种方式让组件声明自己的接口规范。简单说,一个组件应该可以规范以下这些方面:
- 这个组件支持哪些prop;
- 每个prop应该是什么样的格式。
React通过propTypes来支持这些功能 比如,对于Counter组件的propTypes定义代码如下:
Counter.propTypes = {
caption: PropTypes.string.isRequired,
//要求caption必须是string类型
//caption带上了isRequried,这表示使用Counter组件必须要指定caption
initValue: PropTypes.number
//initValue必须是number类型。
};
【注意】 propTypes检查只是一个辅助开发的功能,并不会改变组件的行为。即使在上面propTypes检查出错的情况下,组件依旧能工作。
propTypes虽然能够在开发阶段发现代码中的问题,但是放在产品环境中就不大合适了。babel-react-optimize可以将其在生产环境去掉
React的state
驱动组件渲染过程的除了prop,还有state,state代表组件的内部状态。由于React组件不能修改传入的prop,所以需要记录自身数据变化,就要使用state。
组件的state必须是一个JavaScript对象,不能是string或者number这样的简单数据类型
初始化state
通常在组件类的构造函数结尾处初始化state
constructor(props) {
… this.state = { count: props.initValue || 0
//如果没有,就使用默认值0。
}}
初始值可能被遗漏,那我们会给一个默认的初始值,但是如果默认的初始值被遗漏怎么办,而且一直这样写代码也不美观,那我们可以用React的defaultProps功能,让代码更加容易读懂。
Counter.defaultProps = {
initValue: 0
};
//上述代码写在组件最后
//这样上面的代码就可以简化成:
count: props.initValue
//,即使Counter的使用者没有指定initValue,
在组件中就会收到一个默认的属性值0。
读取和更新state
不要直接修改state,应该使用 setState(),直接修改无法触发渲染,只会改变值,这样页面上就无法看到改变
onClickIncrementButton() {
this.setState({count: this.state.count + 1});
}
prop和state的对比
【注意】 组件是绝不应该去修改传入的props值的。