为了帮助大家快速上手ReactNative开发,在这本节中将向大家介绍开发ReactNative所需要的一些React必备基础知识。
概述本节课将从React的特点、如何使用React、JSX语法,然后会对组件(Component)以及组件的属性(props)、状态(state)、生命周期等方面进行讲解。
通过本节课程能学到什么?- 对React有个全面的认识;
- 熟悉JSX基本语法;
- 了解组件结构;
- 熟悉组件的生命周期;
- 学会使用props;
- 学会使用state;
- 熟悉自定义组件;
React是Facebook推出的开源JavaScriptLibrary,它是一个用于组建用户界面的JavaScript库,让你以更简单的方式来创建交互式用户界面,它的出现让许多革新性的Web观念开始流行起来,例如:VirtualDOM、Component,声明式渲染等。
声明式与命令式
命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
演示
- 当数据改变时,React将高效的更新和渲染需要更新的组件。声明式视图使你的代码更可预测,更容易调试。
- 构建封装管理自己的状态的组件,然后将它们组装成复杂的用户界面。由于组件逻辑是用JavaScript编写的,而不是模板,所以你可以轻松地通过您的应用程序传递丰富的数据,并保持DOM状态。
- 一次学习随处可写,学习React,你不仅可以将它用于Web开发,也可以用于ReactNative来开发Android和iOS应用。
构建一个新的React单页应用,可以通过CreateReactApp来完成。它可以帮助你配置开发环境,以便你可以使用最新的JavaScript特性,还能提供一个友好的开发体验,并为生产环境优化你的应用。
npminstall-gcreate-react-appcreate-react-appmy-appcdmy-appnpmstart
"dependencies":{"react":"^16.6.3",//是React的核心库"react-dom":"^16.6.3",//提供与DOM相关的功能"react-scripts":"2.1.1"//create-react-app的一个核心包,一些脚本和工具的默认配置都集成在里面},
ReactDOM.renderReactDOM.render(element,container[,callback])
渲染一个React元素到由container提供的DOM中,并且返回组件的一个引用(reference)(或者对于无状态组件返回null)。
JSXJSX是一个看起来很像XML的JavaScript语法扩展。每一个XML标签都会被JSX转换工具转换成纯JavaScript代码,使用JSX,组件的结构和组件之间的关系看上去更加清晰。JSX并不是React必须使用的,但React官方建议我们使用JSX,因为它能定义简洁且我们熟知的包含属性的树状结构语法。
Usage:
React.render(//使用JSX<div><div><div>content</div></div></div>,document.getElementById('example'));React.render(//不使用JSXReact.createElement('div',null,React.createElement('div',null,React.createElement('div',null,'content'))),document.getElementById('example'));
createElementReact.createElement(type,[props],[...children])
根据给定的类型创建并返回新的Reactelement。参数type既可以是一个html标签名称字符串(例如'div'或'span'),也可以是一个Reactcomponent类型(一个类或一个函数)。
React.createElement(Hello,{toWhat:'World'},'hello'),//等价于<HellotoWhat="World">hello</Hello>,
HTML标签与React组件对比React可以渲染HTML标签(strings)或React组件(classes)。要渲染HTML标签,只需在JSX里使用小写字母开头的标签名。
varmyDivElement=<divclassName="foo"/>;React.render(myDivElement,document.root);
要渲染React组件,只需创建一个大写字母开头的本地变量。
varMyComponent=...;varmyElement=<MyComponentsomeProperty={true}/>;React.render(myElement,document.body);
提示:
- React的JSX里约定分别使用首字母大、小写来区分本地组件的类和HTML标签。
- 由于JSX就是JavaScript,一些标识符像class和for不建议作为XML属性名。作为替代,ReactDOM使用className和htmlFor来做对应的属性。
要使用JavaScript表达式作为属性值,只需把这个表达式用一对大括号{}
包起来,不要用引号""
。
//输入(JSX):varperson=<Personname={window.isLoggedIn?window.name:''}/>;//输出(JS):varperson=React.createElement(Person,{name:window.isLoggedIn?window.name:''});
子节点表达式同样地,JavaScript表达式可用于描述子结点:
//输入(JSX):varcontent=<Container>{window.isLoggedIn?<Nav/>:<Login/>}</Container>;//输出(JS):varcontent=React.createElement(Container,null,window.isLoggedIn?React.createElement(Nav):React.createElement(Login));
注释JSX里添加注释很容易,它们只是JS表达式而已。你只需要在一个标签的子节点内(非最外层)用{}包围要注释的部分。
classReactDemoextendsComponent{render(){return(<Viewstyle={styles.container}>{/*标签子节点的注释*/}<Textstyle={styles.welcome}//textAlign='right'textShadowColor='yellow'/*color='red'textShadowRadius='1'*/>ReactNative!</Text></View>);}}
心得:在标签节点以外注释,和通常的注释是一样的,多行用“/**/”单行用“//”;
JSX延展属性不要试图去修改组件的属性不推荐做法:
varcomponent=<Component/>;component.props.foo=x;//不推荐component.props.bar=y;//不推荐
这样修改组件的属性,会导致React不会对组件的属性类型(propTypes)进行的检查。从而引发一些预料之外的问题。
推荐做法:
varcomponent=<Componentfoo={x}bar={y}/>;
延展属性(SpreadAttributes)你可以使用JSX的新特性-延展属性:
varprops={};props.foo=x;props.bar=y;varcomponent=<Component{...props}/>;
传入对象的属性会被复制到组件内。
它能被多次使用,也可以和其它属性一起用。注意顺序很重要,后面的会覆盖掉前面的。
varprops={foo:'default'};varcomponent=<Component{...props}foo={'override'}/>;console.log(component.props.foo);//'override'
上文出现的...
标记被叫做延展操作符(spreadoperator)已经被ES6数组支持。
www.devio.org/2018/09/09/…
ComponentReact允许将代码封装成组件(component),然后像插入普通HTML标签一样,在网页中插入这个组件。
classHelloextendsReact.Component{render(){return<h1>Hello{this.props.name}</h1>;}}ReactDOM.render(<Helloname="John"/>,document.getElementById('example'));
上面代码中,变量HelloMessage就是一个组件类。模板插入<HelloMessage/>
时,会自动生成HelloMessage的一个实例。所有组件类都必须有自己的render方法,用于输出组件。
注意
- 组件类的第一个字母必须大写
- 组件类只能包含一个顶层标签?演示
我们可以通过this.props.xx
的形式获取组件对象的属性,对象的属性可以任意定义,但要避免与JavaScript关键字冲突。
this.props.children
会返回组件对象的所有属性。React提供一个工具方法React.Children来处理this.props.children。我们可以用React.Children.map
或React.Children.forEach
来遍历子节点。
React.Children.map
React.Children.map(children,function[(thisArg)])
在包含在children里的每个子级上调用函数,调用的函数的this设置为thisArg。如果children是一个嵌套的对象或数组,它将被遍历。如果children是null或undefined,返回null或undefined而不是一个空数组。
React.Children.forEach
React.Children.forEach(children,function[(thisArg)])
Usage:
classNotesListextendsReact.Component{render(){return(<ol>{React.Children.map(this.props.children,(child)=>{return<h1>{child}</h1>;})}</ol>);}}ReactDOM.render(<NotesList><span>hello</span><span>world</span></NotesList>,document.getElementById('root'));
演示
[PropTypes]组件的属性可以接受任意值,字符串、对象、函数等等都可以。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。组件类的PropTypes属性,就是用来验证组件实例的属性是否符合要求。
React.PropTypes从Reactv15.5开始被移入了prop-types
,使用时需要留意;
importPropTypesfrom'prop-types'classMyTitleextendsReact.Component{staticpropTypes={title:PropTypes.string.isRequired,};render(){return<h1>title:{this.props.title}</h1>;}}ReactDOM.render(<MyTitle/>,document.getElementById('root'));
上面的Mytitle组件有一个title属性。PropTypes告诉React,这个title属性是必须的,而且它的值必须是字符串。现在,我们设置title属性的值是一个数值。
vardata=123;ReactDOM.render(<MyTitletitle={data}/>,document.body);
这样一来,title属性就通不过验证了。控制台会显示一行错误信息。
Warning:FailedpropType:Invalidprop`title`oftype`number`suppliedto`MyTitle`,expected`string`.
更多的PropTypes设置,可以查看官方文档。
默认属性此外,可以通过defaultProps用来设置组件属性的默认值。
classMyTitleextendsReact.Component{staticdefaultProps={shortName:'MyTitle'};render(){return<h1>{this.props.shortName}</h1>;}}ReactDOM.render(<MyTitle/>,document.getElementById('root'));
上面代码会输出"MyTitle"
。
组件并不是真实的DOM节点,而是存在于内存之中的一种数据结构,叫做虚拟DOM(virtualDOM)。只有当它插入文档以后,才会变成真实的DOM。根据React的设计,所有的DOM变动,都先在虚拟DOM上发生,然后再将实际发生变动的部分,反映在真实DOM上,这种算法叫做DOMdiff,它可以极大提高网页的性能表现。
但是,有时需要从组件获取真实DOM的节点,这时就要用到ref属性。
classAlertextendsReact.Component{showAlert(message){alert(`Debug:${message}`);}render(){returnnull;}}classMyTitleextendsReact.Component{onClick=()=>{this.refs.alert.showAlert('MyTitle');};render(){return<div><h1onClick={this.onClick}>Clickme</h1><Alertref='alert'/></div>;}}ReactDOM.render(<MyTitle/>,document.getElementById('root'));
上面代码中,组件MyTitle的子节点有一个Alert组件,为了调用这个组件提供的方法,这时就必须获取真实的DOM节点,虚拟DOM是拿不到用户输入的。为了做到这一点,我们在使用这个组件的时候必须为其设置一个ref属性,然后this.refs.[refName]就会返回这个真实的DOM节点。
需要注意的是,由于this.refs.[refName]属性获取的是真实DOM,所以必须等到虚拟DOM插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定Click事件的回调函数,确保了只有等到真实DOM发生Click事件之后,才会读取this.refs.[refName]属性。
React组件支持很多事件,除了Click事件以外,还有KeyDown、Copy、Scroll等,完整的事件清单请查看官方文档。
心得:ref属性在开发中使用频率很高,使用它你可以获取到任何你想要获取的组件的对象,有了这个对象你就可以灵活地做很多事情,比如:读写对象的变量,甚至调用对象的函数。
state上文讲到了props,组件会根据props的变化来进行渲染,但组件无法改变自身的props,那么组件为了实现交互,可以使用组件的state。state是组件私有的,可以通过state={}方式初始化,通过调用this.setState()
来改变它。当state更新之后,组件就会重新渲染自己。
render()方法依赖于this.props和this.state,框架会确保渲染出来的UI界面总是与输入(this.props和this.state)保持一致。
初始化state可以通过一下两种方式来初始化state,在组件的生命周期中仅执行一次,用于设置组件的初始化state。
constructor(props){super(props);this.state={name:''}}//orstate={name:''}
更新state通过this.setState()
方法来更新state,调用该方法后,React会重新渲染相关的UI。this.setState({favorite:!this.state.favorite});
Usage:
classFavoriteButtonextendsReact.Component{state={favorite:false};handleClick=()=>{this.setState({favorite:!this.state.favorite});};render(){consttext=this.state.favorite?'favorite':'unfavorite';return(<h1onClick={this.handleClick}>You{text}this.Clicktotoggle.</h1>);}}
上面代码是一个FavoriteButton组件,它的通过state={}初始状态,也就是一个对象,这个对象可以通过this.state属性读取。当用户点击组件,导致状态变化,this.setState方法就修改状态值,每次修改以后,自动调用this.render方法,再次渲染组件。
心得:由于this.props和this.state都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props表示那些本组件无法改变的特性,而this.state是会随着用户互动而产生变化的特性。
组件的生命周期在iOS中UIViewController
提供了(void)viewWillAppear:(BOOL)animated
,-(void)viewDidLoad
,(void)viewWillDisappear:(BOOL)animated
等生命周期方法,在Android中Activity
则提供了onCreate()
,onStart()
,onResume()
,onPause()
,onStop()
,onDestroy()
等生命周期方法,这些生命周期方法描述了一个界面从创建到销毁的一生。
那么在React中组件(Component)也是有自己的生命周期方法的。
演示
[组件的生命周期分成三个时期:- Mounting:创建时
- Updating:更新时
- Unmounting:卸载时
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
使用这些生命周期方法通常会导致错误和不一致,因此将来会被弃用。在新的React版本中他们被标记为UNSAFE。
static-lifecycle-methods
Mounting(装载)- constructor();
- staticgetDerivedStateFromProps()
- render()
- componentDidMount()
constructor(props)
React组件的构造函数将会在装配之前被调用。当为一个React.Component子类定义构造函数时,你应该在任何其他的表达式之前调用super(props)。否则,this.props在构造函数中将是未定义,并可能引发异常。
构造函数是初始化状态的合适位置。若你不初始化状态且不绑定方法,那你也不需要为你的React组件定义一个构造函数。
staticgetDerivedStateFromProps()staticgetDerivedStateFromProps(nextProps,prevState)
组件实例化后和接受新属性时将会调用getDerivedStateFromProps。它应该返回一个对象来更新状态,或者返回null来表明新属性不需要更新任何状态。
注意,如果父组件导致了组件的重新渲染,即使属性没有更新,这一方法也会被调用。如果你只想处理变化,那么可以通过比较新旧值来完成。
调用this.setState()通常不会触发getDerivedStateFromProps()。
renderReactComponentrender()
render()
方法是必须的。
当被调用时,其会检查this.props和this.state并返回以下类型中的一个:
- React元素。通常是由JSX创建。该元素可能是一个原生DOM组件的表示,如,或者是一个你定义的复合组件。