彻底理解react中的props
每天对自己多问几个为什么,总是有着想象不到的收获。 一个菜鸟小白的成长之路(copyer)
在react中 state 和 props是两个非常重要的属性,并且常用的属性。简单来说:都是用来保存数据状态的。
需注意的是state,props的每一次改动都会使页面重新渲染一次。
state的解释
state
意为 状态,组件内部的状态。
state
是一个可变的属性,控制组件内部的状态
state
一般用于组件内部的状态维护,更新组建内部的数据,状态,更新子组件的props等
props的解释
Props
是Properties
的简写。
Props
用于组件之间的通信,一种数据传递的方式。由于React的数据流是自上而下的,所以是从父组件向子组件进行传递。
props
是只读属性。想要修改props,只能从父组件修改,然后传递给子组件。
props的形成
都知道, react 中的 jsx
会通过 babel
转化成 createElement
函数。所以,这里主要就是要知道createElement这个函数。话不多说,先看实例,然后看createElement的源码。
jsx转化成createElement的实例(babel官网的转化)
根据上面的转化和图解:
可以看,createElement
接受多个参数,
第一个参数为组件的名称(大写字母
就是组件的名称,小写字母
开头,就是原生的html标签),
第二个参数为props,如果没有props,就是为 null
第三个参数及跟多的参数(可选参数):
- 如果没有子元素,就是
undefined
- 如果有子元素,那么
子元素的createElement的函数
就作为参数,如果有多个子元素,就依次排列下去(跟vue3的h函数
有点区别,h函数的三个参数是数组,存放子元素的h函数)
看完了上面的分析,是不是对React.createElement函数有点认识了啊,那么接下来看看源码是怎么实现的吧。(这个函数不复杂,但是我还是省略一些代码,便于理解)
const RESERVED_PROPS = { // 处理props, 排除特殊的关键字 key ref 等等key: true,ref: true,__self: true,__source: true,
};export function createElement(type, config, children) {let propName;const props = {}; // 定义一个props的空对象//... 省略代码// 遍历 config 赋值给 propsfor (propName in config) { if (hasOwnProperty.call(config, propName) && // propsName在config中!RESERVED_PROPS.hasOwnProperty(propName) // propsName不是关键词) {props[propName] = config[propName];}}}// 获取第二个参数后面的参数个数 const childrenLength = arguments.length - 2;if (childrenLength === 1) { // 如果长度为1,就是一个ReactElementprops.children = children;} else if (childrenLength > 1) { // 如果长度为1, 就是一个数组,保存着多个ReactElementconst childArray = Array(childrenLength);for (let i = 0; i < childrenLength; i++) {childArray[i] = arguments[i + 2];}// ...props.children = childArray;}return ReactElement( // 生成一个ReactElement的虚拟DOMtype,key,ref,self,source,ReactCurrentOwner.current,props,);
}
看望上面的代码,就可以很清楚的了解props的组成。
let props = {}
props是一个对象propName in config
循环createElement的第二个参数config,第二个参数,就是组件中写的propsprops.children
就是组件中是否存在子元素。如果没有子元素,值就是undefined
;如果是有一个子元素,值就是ReactElement元素
;如果有多个,值就是一个数组,数组中存着ReactElement元素
。
props.children
上面的源码解析中已经中分析了,可以继续看上面的分析
defaultProps
在定义中组件的时候,我们可以给组件的props设置默认值
,可以有效的防止没有传递对应的props,是程序报错
。
具体用法:
// 定义组件
const Test = (props) => {const { title } = propsreturn (<div>{title}</div>)
}
Test.defaultProps = {title: 'james'
}//使用组件
const App = () => {return (<div><Test title="kobe" /> </div>)
}
在上面使用Test组件的时候,如果传递了title,那么Test中,title就是kobe
。如果没有传递title,那么组件中的title就是james
。
源码分析
createElement中的一个参数,就是组件的名称(type)
// 源码部分处理默认值
if (type && type.defaultProps) {const defaultProps = type.defaultProps;for (propName in defaultProps) { // 循环默认props,对象if (props[propName] === undefined) { // 如果在props中的值为undefined,使用默认值props[propName] = defaultProps[propName];}}}
displayName
displayName:就是组件取个名字,便于区分。
function Test() {}
Test.displayName = 'OwnTest' // OwnTest用于展示
用途:
- 在react的设置
设计模式中的组合模式
,就需要通过判断传递的子组件来进行渲染。 - 调试工具的调试的名称显示
PropTypes
对组件的props的类型限定。如果JavaScript是个弱语言,会自动进行隐式转化,容易造成react的程序报错。所以,我们就需要对类型进行限定。
所以,prop-types
就很好对组件的类名进行限定。
安装:
npm install --save prop-types
官网地址:
https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html#gatsby-focus-wrapper
示例:
import PropTypes from 'prop-types';class Greeting extends React.Component {render() {return (<h1>Hello, {this.props.name}</h1>);}
}Greeting.propTypes = {name: PropTypes.string
};
类型限定:
import PropTypes from 'prop-types';MyComponent.propTypes = {// 你可以将属性声明为 JS 原生类型,默认情况下// 这些属性都是可选的。optionalArray: PropTypes.array,optionalBool: PropTypes.bool,optionalFunc: PropTypes.func,optionalNumber: PropTypes.number,optionalObject: PropTypes.object,optionalString: PropTypes.string,optionalSymbol: PropTypes.symbol,// 任何可被渲染的元素(包括数字、字符串、元素或数组)// (或 Fragment) 也包含这些类型。optionalNode: PropTypes.node,// 一个 React 元素。optionalElement: PropTypes.element,// 一个 React 元素类型(即,MyComponent)。optionalElementType: PropTypes.elementType,// 你也可以声明 prop 为类的实例,这里使用// JS 的 instanceof 操作符。optionalMessage: PropTypes.instanceOf(Message),// 你可以让你的 prop 只能是特定的值,指定它为// 枚举类型。optionalEnum: PropTypes.oneOf(['News', 'Photos']),// 一个对象可以是几种类型中的任意一个类型optionalUnion: PropTypes.oneOfType([PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)]),// 可以指定一个数组由某一类型的元素组成optionalArrayOf: PropTypes.arrayOf(PropTypes.number),// 可以指定一个对象由某一类型的值组成optionalObjectOf: PropTypes.objectOf(PropTypes.number),// 可以指定一个对象由特定的类型值组成optionalObjectWithShape: PropTypes.shape({color: PropTypes.string,fontSize: PropTypes.number}),// An object with warnings on extra propertiesoptionalObjectWithStrictShape: PropTypes.exact({name: PropTypes.string,quantity: PropTypes.number}),// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保// 这个 prop 没有被提供时,会打印警告信息。requiredFunc: PropTypes.func.isRequired,// 任意类型的必需数据requiredAny: PropTypes.any.isRequired,// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。// 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。customProp: function(props, propName, componentName) {if (!/matchme/.test(props[propName])) {return new Error('Invalid prop `' + propName + '` supplied to' +' `' + componentName + '`. Validation failed.');}},// 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。// 它应该在验证失败时返回一个 Error 对象。// 验证器将验证数组或对象中的每个值。验证器的前两个参数// 第一个是数组或对象本身// 第二个是他们当前的键。customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {if (!/matchme/.test(propValue[key])) {return new Error('Invalid prop `' + propFullName + '` supplied to' +' `' + componentName + '`. Validation failed.');}})
};
哈哈哈,上面的有点多,不用记太多,记住常用的就行了。如果有特殊的需求,就再去查官网吧。
TypeScript对props的限定
在上面,就是对props的类型限定和默认值的设置,在ts中就能完美的解决。
类组件
React.Component<P, S>
P: 就是对props的类型进行限定。
S:就是对state的类型限定
// node_modules/@types/react/index.d.tsclass Component<P, S> {//设置静态属性static contextType?: Context<any>;context: any;//限定props的类型,为readonly类型,只读的constructor(props: Readonly<P>);constructor(props: P, context?: any);setState<K extends keyof S>(state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),callback?: () => void): void;forceUpdate(callback?: () => void): void;//render的类型render(): ReactNode;//对props的类型限定readonly props: Readonly<P> & Readonly<{ children?: ReactNode }>;//state的类型限定state: Readonly<S>;refs: {[key: string]: ReactInstance};}
函数组件
React.FC<IProps>
IProps: 就是对props的类型进行限定
type FC<P = {}> = FunctionComponent<P>;interface FunctionComponent<P = {}> {//对props进行限定, 返回值是一个ReactElement对象,为了生成虚拟DOM(props: PropsWithChildren<P>, context?: any): ReactElement | null;propTypes?: WeakValidationMap<P>;contextTypes?: ValidationMap<any>;defaultProps?: Partial<P>;displayName?: string;
}
props的类型 :PropsWithChildren<P>
这里PropsWithChildren中内部实现了props新增children属性。
总结
目前对props的所有认识,就在这里,以后遇到新的知识,然后在进行补充。如果上面的有错误,可以提出来哟。
附加信息:createElement的完整源码
/*** Create and return a new ReactElement of the given type.* See https://reactjs.org/docs/react-api.html#createelement*/
export function createElement(type, config, children) {let propName;// Reserved names are extractedconst props = {};let key = null;let ref = null;let self = null;let source = null;if (config != null) {if (hasValidRef(config)) {ref = config.ref;if (__DEV__) {warnIfStringRefCannotBeAutoConverted(config);}}if (hasValidKey(config)) {key = '' + config.key;}self = config.__self === undefined ? null : config.__self;source = config.__source === undefined ? null : config.__source;// Remaining properties are added to a new props objectfor (propName in config) {if (hasOwnProperty.call(config, propName) &&!RESERVED_PROPS.hasOwnProperty(propName) // reserved_props) {props[propName] = config[propName];}}}// Children can be more than one argument, and those are transferred onto// the newly allocated props object.const childrenLength = arguments.length - 2;if (childrenLength === 1) {props.children = children;} else if (childrenLength > 1) {const childArray = Array(childrenLength);for (let i = 0; i < childrenLength; i++) {childArray[i] = arguments[i + 2];}if (__DEV__) {if (Object.freeze) {Object.freeze(childArray);}}props.children = childArray;}// Resolve default propsif (type && type.defaultProps) {const defaultProps = type.defaultProps;for (propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}}if (__DEV__) {if (key || ref) {const displayName =typeof type === 'function'? type.displayName || type.name || 'Unknown': type;if (key) {defineKeyPropWarningGetter(props, displayName);}if (ref) {defineRefPropWarningGetter(props, displayName);}}}return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props,);
}
彻底理解react中的props相关推荐
- [react] 为什么说React中的props是只读的?
[react] 为什么说React中的props是只读的? React 组件都必须像纯函数一样保护它们的 props 不被更改. 将react组件理解成纯函数,数据流驱动,参数传入不允许做更改 扩展 ...
- [react] React中验证props的目的是什么?
[react] React中验证props的目的是什么? 对属性进行强检验,避免运行时报错 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌谣一起通 ...
- 如何理解react中的super(),super(props)
用es6的class(类)的方法创建组件: 子类继承父类的属性:需要使用super()继续父类的属性,同时创建this(子类本身没有this); 如果像上图一样需要定义props的属性 this.pr ...
- 理解react中的高阶组件和高阶函数
高阶组件 ES6中使用关键字class来使它的语法有和 类 相似的写法和功能,但是它的本质还是一个函数. 因此,高阶组件本质上是一个函数,只是这种组件(函数)接收一个组件,返回一个新的组件. 比如下面 ...
- React开发(164):React中this.props.children续集
- React开发(163):React中this.props.children
- react中的双向绑定
前言:因为项目原因需要学习另一个超级火的框架react, 因为之前一直使用vue进行开发,所以在学习react中会不自觉的代入一些vue中的概念来理解react中的实现,下面就通过对比学习的方式记录下 ...
- react发送Ajax中文问号,在React中你真的用对了Ajax吗?
通过AJAX加载初始数据 通过AJAX加载数据是一个很普遍的场景.在React组件中如何通过AJAX请求来加载数据呢?首先,AJAX请求的源URL应该通过props传入:其次,最好在component ...
- react 渲染道具_在React中学习分解道具的基础
react 渲染道具 by Evelyn Chan 通过伊芙琳·陈 在React中学习分解道具的基础 (Learn the basics of destructuring props in React ...
最新文章
- 孙鑫VC++课程中用到的函数一览
- PHP基于数组的分页函数(核心函数array_slice())
- 一名新晋程序员的自述:我的编程自学之路
- Dos下命令运行带有包名的Java类
- 三级菜单 ajax 已经测试成功
- apache php 3秒,php版本(5.3,5.5,7.0)及运行模式(fast-cgi/fpm,apache模块)之间性能对比测试...
- 定义返回函数指针(地址)的函数
- 再学 GDI+[41]: 文本输出 - 控制输出字符的个数
- 雷电模拟器7抓包安装证书
- Google Arcore
- Radius 协议介绍
- python:Craps赌博游戏
- [Maven实战-许晓斌]-[第三章] Mave使用入门二(在IDE中的使用) [第四章] 案例的背景介绍...
- 用 LSTM 预测股票价格
- 饥荒如何解锁机器人_《饥荒》全部人物怎样解锁 全人物解锁条件及方法一览...
- VS1003调试例程
- 【游戏】LOL只能攻击英雄,点不了小兵解决办法
- Python小课们是如何赚钱的
- c 语言多个if并联使用,if的嵌套和多个并列if的效率有关问题
- Harray Potter and the Sorcerer's Stone