为了帮助你们快速上手React Native开发,在这本节中将向你们介绍开发React Native所须要的一些React必备基础知识。javascript
本节课将从React的特色、如何使用React、JSX语法,而后会对组件(Component)以及组件的属性(props)、状态(state)、生命周期等方面进行讲解。html
React 是 Facebook 推出的开源 JavaScript Library,它是一个用于组建用户界面的JavaScript库,让你以更简单的方式来建立交互式用户界面,它的出现让许多革新性的 Web 观念开始流行起来,例如:Virtual DOM、Component,声明式渲染等。java
声明式与命令式node
命令式编程:命令“机器”如何去作事情(how),这样无论你想要的是什么(what),它都会按照你的命令实现。
声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去作(how)。
复制代码
演示react
构建一个新的 React 单页应用,能够经过Create React App来完成。它能够帮助你配置开发环境,以便你可使用最新的 JavaScript 特性,还能提供一个友好的开发体验,并为生产环境优化你的应用。git
npm install -g create-react-app
create-react-app my-app
cd my-app
npm start
复制代码
"dependencies": {
"react": "^16.6.3",//是 React 的核心库
"react-dom": "^16.6.3",//提供与 DOM 相关的功能
"react-scripts": "2.1.1"//create-react-app 的一个核心包,一些脚本和工具的默认配置都集成在里面
},
复制代码
ReactDOM.render(element, container[, callback])github
渲染一个 React 元素到由 container 提供的 DOM 中,而且返回组件的一个 引用(reference) (或者对于 无状态组件 返回 null )。算法
JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。 每个XML标签都会被JSX转换工具转换成纯JavaScript代码,使用JSX,组件的结构和组件之间的关系看上去更加清晰。 JSX并非React必须使用的,但React官方建议咱们使用 JSX , 由于它能定义简洁且咱们熟知的包含属性的树状结构语法。npm
Usage:编程
React.render(//使用JSX
<div>
<div>
<div>content</div>
</div>
</div>,
document.getElementById('example')
);
React.render(//不使用JSX
React.createElement('div', null,
React.createElement('div', null,
React.createElement('div', null, 'content')
)
),
document.getElementById('example')
);
复制代码
React.createElement(
type,
[props],
[...children]
)
复制代码
根据给定的类型建立并返回新的 React element 。参数type既能够是一个html标签名称字符串(例如'div' 或 'span' ),也能够是一个 React component 类型(一个类或一个函数)。
React.createElement(Hello, {toWhat: 'World'}, 'hello'),
//等价于 <Hello toWhat="World">hello</Hello>,
复制代码
React 能够渲染 HTML 标签 (strings) 或 React 组件 (classes)。 要渲染 HTML 标签,只需在 JSX 里使用小写字母开头的标签名。
var myDivElement = <div className="foo" />;
React.render(myDivElement, document.root);
复制代码
要渲染 React 组件,只需建立一个大写字母开头的本地变量。
var MyComponent = ...;
var myElement = <MyComponent someProperty={true} />;
React.render(myElement, document.body);
复制代码
提示:
- React 的 JSX 里约定分别使用首字母大、小写来区分本地组件的类和 HTML 标签。
- 因为 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议做为 XML 属性名。做为替代, React DOM 使用 className 和 htmlFor 来作对应的属性。
要使用 JavaScript 表达式做为属性值,只需把这个表达式用一对大括号 {}
包起来,不要用引号 ""
。
// 输入 (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// 输出 (JS):
var person = React.createElement(
Person,
{name: window.isLoggedIn ? window.name : ''}
);
复制代码
一样地,JavaScript 表达式可用于描述子结点:
// 输入 (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// 输出 (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
复制代码
JSX 里添加注释很容易,它们只是 JS 表达式而已。你只须要在一个标签的子节点内(非最外层)用 {} 包围要注释的部分。
class ReactDemo extends Component {
render() {
return (
<View style={styles.container}>
{/*标签子节点的注释*/}
<Text style={styles.welcome} //textAlign='right' textShadowColor='yellow' /*color='red' textShadowRadius='1'*/ >
React Native!
</Text>
</View>
);
}
}
复制代码
心得:在标签节点之外注释,和一般的注释是同样的,多行用“/**/” 单行用“//”;
不推荐作法:
var component = <Component />;
component.props.foo = x; // 不推荐
component.props.bar = y; // 不推荐
复制代码
这样修改组件的属性,会致使React不会对组件的属性类型(propTypes)进行的检查。从而引起一些预料以外的问题。
推荐作法:
var component = <Component foo={x} bar={y} />;
复制代码
你可使用 JSX 的新特性 - 延展属性:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
复制代码
传入对象的属性会被复制到组件内。
它能被屡次使用,也能够和其它属性一块儿用。注意顺序很重要,后面的会覆盖掉前面的。
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'
复制代码
上文出现的...
标记被叫作延展操做符(spread operator)已经被 ES6 数组 支持。
React 容许将代码封装成组件(component),而后像插入普通 HTML 标签同样,在网页中插入这个组件。
class Hello extends React.Component{
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
ReactDOM.render(
<Hello name="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:
class NotesList extends React.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属性,就是用来验证组件实例的属性是否符合要求。
React.PropTypes 从 React v15.5开始被移入了
prop-types
,使用时须要留意;
import PropTypes from 'prop-types'
class MyTitle extends React.Component{
static propTypes={
title: PropTypes.string.isRequired,
};
render() {
return <h1> title:{this.props.title} </h1>;
}
}
ReactDOM.render(<MyTitle />, document.getElementById('root'));
复制代码
上面的Mytitle组件有一个title属性。PropTypes 告诉 React,这个 title 属性是必须的,并且它的值必须是字符串。如今,咱们设置 title 属性的值是一个数值。
var data = 123;
ReactDOM.render(
<MyTitle title={data} />,
document.body
);
复制代码
这样一来,title属性就通不过验证了。控制台会显示一行错误信息。
Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.
更多的PropTypes设置,能够查看官方文档。
此外,能够经过defaultProps用来设置组件属性的默认值。
class MyTitle extends React.Component{
static defaultProps={
shortName:'MyTitle'
};
render() {
return <h1> {this.props.shortName}</h1>;
}
}
ReactDOM.render(<MyTitle/>, document.getElementById('root'));
复制代码
上面代码会输出"MyTitle"
。
组件并非真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫作虚拟 DOM (virtual DOM)。只有当它插入文档之后,才会变成真实的 DOM 。根据 React 的设计,全部的 DOM 变更,都先在虚拟 DOM 上发生,而后再将实际发生变更的部分,反映在真实 DOM上,这种算法叫作 DOM diff ,它能够极大提升网页的性能表现。
可是,有时须要从组件获取真实 DOM 的节点,这时就要用到 ref 属性。
class Alert extends React.Component {
showAlert(message) {
alert(`Debug:${message}`);
}
render() {
return null;
}
}
class MyTitle extends React.Component {
onClick = () => {
this.refs.alert.showAlert('MyTitle');
};
render() {
return <div>
<h1 onClick={this.onClick}>Click me</h1>
<Alert ref='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属性在开发中使用频率很高,使用它你能够获取到任何你想要获取的组件的对象,有了这个对象你就能够灵活地作不少事情,好比:读写对象的变量,甚至调用对象的函数。
上文讲到了props,组件会根据props的变化来进行渲染,但组件没法改变自身的props,那么组件为了实现交互,可使用组件的 state 。state 是组件私有的,能够经过state={}方式初始化,经过调用 this.setState()
来改变它。当 state 更新以后,组件就会从新渲染本身。
render() 方法依赖于 this.props 和 this.state ,框架会确保渲染出来的 UI 界面老是与输入( this.props 和 this.state )保持一致。
能够经过一下两种方式来初始化state,在组件的生命周期中仅执行一次,用于设置组件的初始化 state 。
constructor(props){
super(props);
this.state={
name:''
}
}
//or
state={
name:''
}
复制代码
经过this.setState()
方法来更新state,调用该方法后,React会从新渲染相关的UI。 this.setState({favorite:!this.state.favorite});
Usage:
class FavoriteButton extends React.Component{
state={
favorite:false
};
handleClick=()=>{
this.setState({favorite:!this.state.favorite});
};
render(){
const text=this.state.favorite? 'favorite':'un favorite';
return (
<h1 onClick={this.handleClick}> You {text} this. Click to toggle. </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)也是有本身的生命周期方法的。
使用这些生命周期方法一般会致使错误和不一致,所以未来会被弃用。在新的React版本中他们被标记为UNSAFE。
constructor(props)
复制代码
React组件的构造函数将会在装配以前被调用。当为一个React.Component子类定义构造函数时,你应该在任何其余的表达式以前调用super(props)。不然,this.props在构造函数中将是未定义,并可能引起异常。
构造函数是初始化状态的合适位置。若你不初始化状态且不绑定方法,那你也不须要为你的React组件定义一个构造函数。
static getDerivedStateFromProps(nextProps, prevState)
复制代码
组件实例化后和接受新属性时将会调用getDerivedStateFromProps。它应该返回一个对象来更新状态,或者返回null来代表新属性不须要更新任何状态。
注意,若是父组件致使了组件的从新渲染,即便属性没有更新,这一方法也会被调用。若是你只想处理变化,那么能够经过比较新旧值来完成。
调用this.setState() 一般不会触发 getDerivedStateFromProps()。
ReactComponent render()
复制代码
render()
方法是必须的。
当被调用时,其会检查this.props 和 this.state并返回如下类型中的一个:
返回null 或 false时,ReactDOM.findDOMNode(this) 将返回 null。
render()
函数应该是纯粹的,也就是说该函数不修改组件的state
,每次调用都返回相同的结果,不读写 DOM 信息,也不和浏览器交互(例如经过使用setTimeout
)。若是须要和浏览器交互,在componentDidMount()
中或者其它生命周期方法中作这件事。保持render()
纯粹,可使服务器端渲染更加切实可行,也使组件更容易被理解。
提示:若 shouldComponentUpdate()返回false,render()函数将不会被调用。
componentDidMount()
复制代码
componentDidMount()在组件被装配后当即调用,一般在该方法中进行一些初始化操做。·初始化时须要DOM节点的操做能够放到这里进行`。若你须要从远端加载数据,这是一个适合实现网络请求的地方。在该方法里设置状态将会触发重渲。
这一方法是一个发起任何订阅的好地方。若是你这么作了,别忘了在componentWillUnmount()退订。
另外,在这个方法中调用setState()将会触发一次额外的渲染,可是它将在浏览器刷新屏幕以前发生。这保证了即便render()将会调用两次,但用户不会看到中间状态。
shouldComponentUpdate(nextProps, nextState)
复制代码
在接收到新的 props 或者 state,将要渲染以前调用,以让React知道当前状态或属性的改变是否不影响组件的输出。
该方法在初始化渲染的时候不会调用,在使用 forceUpdate 方法的时候也不会。若是肯定新的 props 和 state 不须要从新渲染,则此处应该 返回 false。
心得:重写次方你能够根据实际状况,来灵活的控制组件当 props 和 state 发生变化时是否要从新渲染组件。
getSnapshotBeforeUpdate(prevProps, prevState)
复制代码
getSnapshotBeforeUpdate()在最新的渲染输出提交给DOM前将会当即调用。它让你的组件能在当前的值可能要改变前得到它们。这一辈子命周期返回的任何值将会 做为参数被传递给componentDidUpdate()。
componentDidUpdate(prevProps, prevState, snapshot)
复制代码
在组件的更新已经同步到 DOM 中以后马上被调用。
该方法不会在初始化渲染的时候调用。使用该方法能够在组件更新以后操做 DOM 元素。
componentWillUnmount()
复制代码
在组件从 DOM 中移除的时候马上被调用。
在该方法中执行任何须要的清理,好比无效的定时器,或者清除在 componentDidMount 中建立的 DOM 元素。