React-高级教程完整版
2017-04-05 23:01
204 查看
React 高级教程完整版
这标题可能有点不太贴切或符合内容,从官方上来区分这部分内容确实属于高级部分,只是由于个人原因,在后面的一些章节并没有记录在列。也为了承接上一篇,因此勉强将标题定位:“React 高级教程完整版”
纯属针对个人学习记录成果,无他~~~
属性类型检测(Typechecking With PropTypes)
React内置了一系列的类型那个检测功能,通过
Comp.propTypes对象指定,比如:
Comp.propTypes = { name: React.PropTypes.string };
经过上面的指定之后,如果传入的
name为非字符串的,会报错。
看个小示例:
// props-type-checking.html class TypeCheck extends React.Component { constructor(props) { super(props); } render() { return ( <div>incoming prop: {this.props.sayHello}</div> ); } } // 类型检测在类外面指定 TypeCheck.propTypes = { sayHello: React.PropTypes.string }; ReactDOM.render( <TypeCheck sayHello={11111} />, document.getElementById('root') );
报错内容:
Warning: Failed prop type: Invalid prop
sayHelloof type
numbersupplied to
TypeCheck, expected
stringin TypeCheck
检测类型:
类型 | 属性 |
---|---|
数组 | React.PropTypes.array |
Boolean值 | React.PropTypes.bool |
函数 | React.PropTypes.func |
数字 | React.PropTypes.number |
对象 | React.PropTypes.object |
字符串 | React.PropTypes.string |
符号 | React.PropTypes.symbol |
DOM元素 | React.PropTypes.node |
React元素 | React.PropTypes.element |
某一个类的实例 | React.PropTypes.instanceOf(Message) |
属性值限定在某一范围 | React.PropTypes.oneOf([‘News’, ‘Photos’]), |
多种类型的一种 | React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, React.PropTypes.instanceOf(Message) ]) |
数组且限定数组成员类型 | React.PropTypes.arrayOf(React.PropTypes.number) |
对象且限定对象成员值类型 | React.PropTypes.objectOf(React.PropTypes.number) |
特定形状的对象 | React.PropTypes.shape({ color: React.PropTypes.string, fontSize: React.PropTypes.number }) |
isRequired来指定该属性是否必须,不如指定了而没有传递该属性,则会报下面错误
Warning: Failed prop type: The prop
sayHellois marked as required in
TypeCheck, but its value is
undefinedin TypeCheck
如果直接使用
React.PropTypes.isRequired也会报错:
Warning: Failed prop type: TypeCheck: prop type
sayHellois invalid; it must be a function, usually from React.PropTypes. in TypeCheck
上面意思大概就是:
sayHello类型无效,必须是个函数,通常来自
React.PropTypes。
PropTypes: ReactPropTypes,
会发现
ReactPropTypes其实是个包含类型检测函数的对象
var ReactPropTypes = { array: createPrimitiveTypeChecker('array'), bool: createPrimitiveTypeChecker('boolean'), func: createPrimitiveTypeChecker('function'), number: createPrimitiveTypeChecker('number'), object: createPrimitiveTypeChecker('object'), string: createPrimitiveTypeChecker('string'), symbol: createPrimitiveTypeChecker('symbol'), any: createAnyTypeChecker(), arrayOf: createArrayOfTypeChecker, element: createElementTypeChecker(), instanceOf: createInstanceTypeChecker, node: createNodeChecker(), objectOf: createObjectOfTypeChecker, oneOf: createEnumTypeChecker, oneOfType: createUnionTypeChecker, shape: createShapeTypeChecker };
从上面可知
ReactPropTypes对象下是没有
isRequired这个属性的,再往下看,会发现其实
isRequired是挂接在上面的执行结果之后的,也就是说就算指定了
isRequired也会线执行上面的类型检查,然后再去根据
isRequired值去进一步检测是类型不对还是压根没有传这个属性;
function createChainableTypeChecker(validate) { // 省略 ...... function checkType(isRequired, props, propName, componentName, location, propFullName, secret) { if (props[propName] == null) { var locationName = ReactPropTypeLocationNames[location]; if (isRequired) { if (props[propName] === null) { return new PropTypeError('The ' + locationName + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.')); } return new PropTypeError('The ' + locationName + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.')); } return null; } else { return validate(props, propName, componentName, location, propFullName); } } 1. 先传入 false 执行基本的类型检查 validate,得到检查后的结果,并且结果有两种 1. 属性存在,但是类型错误,这时候会直接返回错误对象 2. 属性不存在,则会返回错误信息,并且继续执行下面一句 var chainedCheckType = checkType.bind(null, false); 2. 这一步执行 `isRequired` 检测,并且是经过上面一步类型检测之后,原因在于如果不添加 `isRequired` 在类型错误的时候由上面检测返回错误结果。如果有那么下面的检测结果中的错误会替代上面一步中 validate 返回的错误检测结果 chainedCheckType.isRequired = checkType.bind(null, true); return chainedCheckType; }
最终在对象
ReactPropTypes中的成员值就是上面函数返回的
chainedCheckType对象,里面包含了错误信息对象和
isRequired(这个也是个
PropTypeError对象,或者
null);
比如上面如果这样指定
sayHello: React.PropTypes.string.isRequired
其实最后的结果就是
chainedCheckType.isRequired这个的结果值,我们的错误信息也就是这里面来的。
上面是个人对源码中
isRequired的处理逻辑的大概理解。
下面是官方提供的两个自定义属性检测的示例
简单属性检测:
// You can also specify a custom validator. It should return an Error // object if the validation fails. Don't `console.warn` or throw, as this // won't work inside `oneOfType`. customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }
定义也比较简单,需要注意的是上面的注释,让我们报错的时候不要使用
console.warn或者
throw方式,因为在
oneOfType里面不能正常工作。
数组(或对象)的检测自定义:
// You can also supply a custom validator to `arrayOf` and `objectOf`. // It should return an Error object if the validation fails. The validator // will be called for each key in the array or object. The first two // arguments of the validator are the array or object itself, and the // current item's key. customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) { if (!/matchme/.test(propValue[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }) };
下面来尝试下自定义方式检测属性
属性值中必须包含
Hello字符串
把上面的示例改一下
// props-type-checking.html TypeCheck.propTypes = { sayHello: function (props, propName, componentName) { if ( !/Hello/.test(props[propName]) ) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } } };
控制台输出错误:(整好跟我们自定义的结果相同)
Warning: Failed prop type: Invalid prop
sayHellosupplied to
TypeCheck. Validation failed. in TypeCheck
然后把属性值修改成如下:(把
hello改成首字母大写
Hello)
ReactDOM.render( <TypeCheck sayHello='Hello world!' />, document.getElementById('root') );
控制台没有错误输出,说明我们自定义的类型检测函数成功。
defaultProps默认属性值对象
除了属性值检测以外,我们还可以通过
defaultProps来指定属性的默认值,使用方法;
Comp.defaultProps = { name: 'lizc' };
指定之后,如果没有传入该属性则会启用该默认属性值。
Refs
和 DOM
(Refs and the DOM)
在典型的 React数据流中,
props是唯一一种能让父组件与子组件进行沟通的方式,比如去修改子组件,刷新重绘子组件都需要用到
props属性对象;但是,有些情况必须要在组件外部或者组件渲染完成之后去引用它,那这个时候
props就显得无能为力了,因为这个属性对象只能在组件内部使用。
因此就有
refs这个东西,其实更具体点,这个
ref对象可以视为未渲染或者渲染之后的组件对象
如果没有渲染那么代表的是
react组件,
如果组件被渲染之后
ref指向了 DOM 书中的该组件对象;
ref使用时机
如果能用属性来控制的,尽量避免使用
ref。
管理组件焦点(focus),文本选择(text selection)或者媒体播放(media playback)
触发必须执行的动画
集成第三方
DOM库
给
DOM元素添加
ref(引用)
在定义组件的时候,可以给组件添加一个
ref属性,这个属性值是一个回调函数,这个函数会在组件被加载完成或者卸载完成之后调用
这个
ref属性其实也可以直接指定成字符串,比如:
<NameTextComp ref="nameText" />,然后可以通过在组件外可以通过
this.refs.nameText去访问这个
DOM节点,但是这么使用会有诸多的问题,比如该链接中出现的问题:issues,所以不推荐直接使用字符串形式,而是使用回调函数形式去使用它
类组件示例
// component-ref-attr.html class CustomInput extends React.Component { constructor(props) { super(props); this.focus = this.focus.bind(this); } focus() { this.textInput.focus(); this.textInput.value = 'I am focused.'; console.log( this.textInput ); } render() { return ( <div> <input type="text" ref={(input) =>{this.textInput = input;}} /> <input type="button" value="click me" onClick={this.focus} /> </div> ); } } ReactDOM.render( <CustomInput />, document.getElementById('root') );
点击按钮之前:
点击按钮之后:
看下控制台输出:
结果显而易见,因为输入框被加载完成之后,会去执行
ref={(input) =>{this.textInput = input;}}
ref里面的函数就是把当前对象(DOM树中的
input元素对象缓存到了
this.textInput)
上面例子是直接在 HTML 元素上加的,其实对于组件也是一样的处理,就不多介绍了,来看下函数式组件定义里面怎么用这个
ref绑定组件的
函数式组件示例
// component-ref-attr.html function CustomFunctionalInput() { let textInput = null; function focus() { console.log( textInput ) textInput.focus(); textInput.value = 'I am focused.'; } return ( <div> <input type="text" ref={(input) =>{textInput = input;}} /> <input type="button" value="click me" onClick={focus} /> </div> ); } ReactDOM.render( <CustomFunctionalInput />, document.getElementById('root') );
从上面示例看,函数式组件使用
ref的关键就是在组件内部定义一个缓存
ref指向的组件的局部变量,实验结果和上例一样。
最后尝试了下在
React组件之上添加
ref去控制子组件里面的
HTML元素,结果是还是需要组建内去另外维护一份自己的
ref,指向具体的
HTML元素。
比如下面:
// component-ref-attr.html // 子组件 class ButtonInput extends React.Component { constructor(props) { super(props); this.btnClick = this.btnClick.bind(this); } btnClick(e) { this.props.btnClick(e); } render() { return ( <div> <input type="text" /> <input type="button" value="click me" onClick={this.props.btnClick} /> </div> ); } } // 父组件 class CustomInput extends React.Component { constructor(props) { super(props); this.focus = this.focus.bind(this); } focus() { // this.textInput.focus(); // this.textInput.value = 'I am focused.'; console.log( this.btnInput ); } render() { return ( <ButtonInput ref={(btnInput) => {this.btnInput = btnInput;}} btnClick={this.focus} /> ); } }
将
this.btnInput输出到控制台得到对象如图
从控制台对象中可知并没有发现
DOM树中的
input/text和
input/button,这是不是意味着并不能直接通过组件对象去获取 DOM 树中的实际元素对象。
如果非要使用
ref特性去访问组件下的组件中的
DOM元素,估计只能在被包含的组件中也去设置个
ref属性,比如上例可修改如下:
在
ButtonInput组件的按钮点击事件处理函数中添加如下:
btnClick(e) { this.props.btnClick(e); this.btnInput.focus(); this.btnInput.value='I am focused by parent.'; }
然后
input元素属性添加:
<input type="text" ref={(btnInput) => {this.btnInput = btnInput;}} />
这样便可以实现,多级组件控制
html元素,验证结果OK。
不可控组件,如:表单(Uncontrolled Components)
这里不可控组件指的是一些组件会有自己的一些默认行为,如表单提交动作,这个时候可能需要拦截提交动作,把表单中的数据进行处理之后再去提交,这个时候就需要使用到‘可控组件’了。表单的可控组件关键在于提交事件拦截
// uncontrolled-component.html class ControlledForm extends React.Component { constructor(props) { super(props); this.submitHandle = this.submitHandle.bind(this); } submitHandle( e ) { this.nameTextInput.value = 'submit form'; e.preventDefault(); } render() { return ( <form onSubmit={this.submitHandle}> <label> <input type="text" ref={(input) => {this.nameTextInput = input;}} /> <input type="submit" value="submit" /> </label> </form> ); } }
对于表单来说,可控组件的实现,关键有两点:
通过
ref属性去引用组件(其实也可以通过组件的状态值去设置)
阻止其默认行为,自定义处理事件
默认值:defaultValue
对于元素都有其默认的一些属性,比如:type=radio或
type=checkbox的
defaultChecked
type=select或
type=text的
defaultValue
大部分情况下使用的都是
defaultValue。
会发现如果我们直接在元素上添加
value属性并赋予值之后,其内容没法通过输入去修改,也就是说被固定了,只能通过代码去修改
ele.value属性
但是如果给元素绑定了事件,比如:
onChange,这个时候值就能发生改变了,这个在之前基础篇的时候有说过。
如果指定的是
value不是
defaultValue的话,就需要使用到
state状态值,同时指定相应的事件处理。
class ControlledForm extends React.Component { constructor(props) { super(props); this.submitHandle = this.submitHandle.bind(this); } submitHandle( e ) { this.nameTextInput.value = 'submit form'; e.preventDefault(); } render() { return ( <form onSubmit={this.submitHandle}> <label> <input type="text" defaultValue="lizc" ref={(input) => {this.nameTextInput = input;}} /> <input type="submit" value="submit" /> </label> </form> ); } }
如果用到
value就需要结合
state和
onChange处理
// 修改点 1 : 构造器中添加状态对象 this.state = { nameText: 'lizc' }; // 修改点 2 : 给输入框元素添加事件绑定 nameTextInputChange( e ) { this.setState({ nameText: e.target.value }); } // 修改点 3 : input 元素的 value 属性指定为状态中的值 this.state.nameText <input value={this.state.nameText} onChange={this.nameTextInputChange}/>
总结
清明节刚过,整个人累的不要不要的,不过宝宝开心的不要不要的,深圳湾共享单车问题也是闹得不要不要的,不过我们是放假第一天就去了,很庆幸的躲过了这一坑,喜剧性的事情是节后第一天小区门口居然贴上了:“禁止单车入园区” 一告示,逗得不要不要的 ~~~~。话说,也有几天没更新了,还是要坚持自己的选择一步一个脚印去实行,虽然近期换工作结果非常不理想(可能是工作履历的原因,也可能是自己没啥对方需要的实际经验,也可能是面试过程中表现不理想),总之自己的果自己尝,努力不放弃就对了。
状态还是有点灰心的,简历如实写了,连面试电话都接不到几个,打击还是有的,信心还是要时刻保持着,学习进度还是继续保持下去,毕竟回头路都没那么好走。
发泄了一丢丢小情绪小心情,该收拾收拾~~~
该篇文章算是承接上一篇《React 基础教程完整版》的下一学习篇的记录,这里涉及的内容主要涉及内容还是
props,
state,
refs三个内容来讲述的,至于更多的有关性能优化,在不使用 ES6 或 JSX 时候的替代方法,等等只是看了个大概,感觉并没有太多记录的必要(毕竟还是要紧跟步伐,ES6 和 JSX 还是要实际使用起来,熟练运用)。
另外需要提的是关于
React更新比较算法的问题,值得看看。
接下来的计划是针对状态管理的框架,比如:
Redux进行研究学习,结合
React投入使用当中。
相关文章推荐
- React高级教程(es6)——(2)对于Refs最新变动的理解
- React-基础教程完整版
- React中文教程 - Advanced Components(组件高级使用)
- React高级教程(es6)——(3)React中的Context
- 大前端全套完整教程106G 爱前端-邵山欢课程 node+angular+vue+react+webapp 高级部分
- React高级教程(es6)——(4)ShouldComponentUpdate的用法
- React高级教程(es6)——(1)JSX语法深入理解
- React高级教程React中的Context
- javascript高级教程
- asp.net高级教程
- javascript教程 - 第五部分 高级话题
- 【完整版】Prototype实战教程 1-16
- CSS高级教程 页面布局
- J2ME中文教程 3 MIDP高级UI 的使用
- CSS高级教程 伪元素
- vs2005视频教程 之 TreeView高级使用 [视频]
- Ghost高级教程
- 【完整版】Prototype实战教程 1-16
- asp.net高级教程(二)--- 转换编程思维
- asp.net高级教程(四)---实战篇(上)