一、ReactDOM.render()方法
ReactDOM.render()方法是React框架中最最最基础的方法,这个方法的作用是将模板转化为HTML的语法,然后插入到指定的 DOM 节点中。
<script type="text/babel">
ReactDOM.render(
<p>Hello, world!</p>,
document.getElementById('test')
);
</script>
上面的代码将一个
插入到 test 的节点中,其中document.getElementById(‘test’)指定被插入的节点。
二、JSX的语法
我们上面就在将JSX语法,那么到底什么是JSX语法呢,其实我们可以简单的认为将HTML语言直接写在JavaScript语言之中,不加任何的引号,JSX语法允许HTML和JavaScript的混写。
var name =['kim' , 'tom' , 'tony'];
ReactDOM.render(
<div>
{
name.map(function(name){
return <div> hello {name}!</div>
})
}
</div>,
document.getElementById('test');
);
总结一下:遇到HTML标签,就使用HTML的规则解析;遇到代码块,使用{}包裹的代码,就用JavaScript规则进行解析。
当然JSX允许直接在模板中插入JavaScript变量,如果这个变量是一个数组的话,我们会展开这个数组的所有的成员。
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
会在结果中打出Hello world!,React is awesome两个标题。
三、组件
React框架允许我们将代码封装成组件,然后像插入普通的HTML标签一样,在网页中插入这个组件。
var HelloMessage = React.creatClass({
render :function(){
return <h1>hello {this.props.name}</h1>
}
});
ReactDOM.render(
<HelloMessage name ='kim' />
document.getElement('test');
)
在上面的代码里面,HelloMessage 就是一个组件类,我们在模板中直接插入了就会自动的生成HelloMessage 的实例,在React中所有的组件都必须有自己的render方法,这个方法的目的是输出组件。
ps:组件的第一个字母必须大写,否则就会报错,而且一个组件类智能包含一个顶级标签,否则也会报错。举个栗子:
var HelloMessage = React.createClass({
render: function() {
return <h1>
Hello {this.props.name}
</h1><p>
some text
</p>;
}
});
上面的代码就会报错,以为在一个组件类中包含了2个顶层标签,<p>
和<h1>
标签。组件的用法和原生的 HTML
标签相比没有任何的区别,可以加入任意的属性,上例中我们就在 HelloMessage 标签中加入了 name 属性。其中组件的属性可以在组件类的 this.props 对象上获取。在添加组件属性的时候,也需要注意class要写成 className ,for 方法要写成 htmlFor ,因为 class 和 for 是 JavaScript 的保留字。this的指向:this指向的是组件类。
this.props.children
this.props
对象的属性和组件的属性是一一对应的,但是还是有一个例外的,那就是 this.props.children
实行。他表示组件所有的子节点。
var NotesList = React.createClass({
render: function() {
return (
<ol>
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
</ol>
);
}
});
ReactDOM.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.body
);
实现的结果是:
this.props.children
的实现结果
ps:this.props.children
的值可能存在三种情况,第一种就是当前的组件没有子节点,那么 this.props.children
返回的就是 undefined
,如果只有一个节点的话,那么返回的数据类型就是Object;如果有多个节点的话,返回的数据类型就是一个数组 array
,所以处理 this.props.children
的时候要格外的小心。
为了解决上述的问题,React框架为我们提供了一个 React.Children
的方法来处理 this.props.children
。我们可以根据 React.Children.map
来进行子节点的遍历。不用再担心 React.Children
的数据类型究竟是undefined
还是 object
。
PropTypes
组件的属性在一定的意义上有一点类似于函数的参数,所以组件的属性值可以接受任意值,字符串、对象、数组、函数等等都可以。所以我们现在需要一种机制来验证别人传过来的值是否符合要求。组件的PropTypes属性,就可以用来检测组件的实例属性是否符合要求。
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return
{this.props.title}
;}
});
上面这段代码的意思就是在MyTitle组件有一个title的属性。PropTypes告诉React,这个title的属性是必须的,而且这个属性的数据类型是字符串,如果我们为title的属性设置为number类型的话,验证就无法通过,会在控制台输出一行错误信息。
还有一个关于属性的就是getDefaultProps方法可以用来设置组件属性的默认值。
var MyTitle = React.createClass({
getDefaultProps : function () {
return {
title : 'Hello World'
};
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
ReactDOM.render(
<MyTitle />,
document.body
);
这个方法的意思就是在没有显示的对title的值就行定义的话,我们就直接使用默认值,也就是说上面的代码默认显示的是‘Hello World’。
获取真实的DOM节点
我们在写的时候讲组件当做真正的HTML的语法来写,但是其实组件并不是真正的DOM节点,而是存在于内存之后的一种数据机构,也就是虚拟DOM,只有将它插入到文档之后,它才会变成真正的DOM,在React设计的本意是,所有的DOM的变动是先发生在虚拟DOM中的,然后再将实际发生变动的部分,反映在真实的DOM中。
但是,有时候,我们需要从组件中获取真实的DOM,这个时候就要用到ref属性了。
var MyComponent = React.createClass({
handleClick: function() {
this.refs.myTextInput.focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('test')
);
组件MyComponent的子节点有一个文本输入框,来得到用户的输入,现在这个情况就是必须获取真实的DOM节点,虚拟DOM对于用户来说是拿不到的,所以我们为了可以获取到这个值,文本框必须有一个ref属性,然后在使用this.refs.[refName]就可以返回这个真实DOM节点。由于this.refs.[refName]属性获取的是真正的DOM,所以一定要等到虚拟的DOM插入文档以后,才可以使用这个属性,要不然就会报错。在上面代码中指定了click回调函数,确保了DOM获取发生在Click之后,才会读取this.refs.[refName]属性。重点就是一定要在虚拟DOM插入到文档之后再进行操作。
this.state
组件是无法避免的要与用户进行互动,我们可以将组件看做是一个状态机,一开始有一个初始状态,然后与用户进行互动,不断的导致状态的改变,从而触发重新的渲染UI。
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
ReactDOM.render(
<LikeButton />,
document.getElementById('test')
);
解释一下上面的代码:LikeButton 这个组件,他有一个getInitialState方法定义了初始状态,这也是一个对象,这个对象可以通过this.state属性读取。当用户点击组件的时候,状态发生了改变,this.setState方法用来进行状态的修改。每一次状态发生改变,就会自动的调用this.render方法,再次进行组件的渲染。
this.props表示哪些属性一旦被定义,就不能被修改;而this.state会随着用户的互动而大声变化的特性。
四、表单
用户在填写表单的时候,属于用户和组件之间的互动,所以不能用this.props来进行读取。
var Input = React.createClass({
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function () {
var value = this.state.value;
return (
<div>
<input type="text" value={value} onChange={this.handleChange} />
<p>{value}</p>
</div>
);
}
});
ReactDOM.render(<Input/>, document.body);
文本框中的值,不能用this.props.value来进行读取,而要定义一个onchange事件的回调函数。通过 event.target.value来读取用户输入的值。
五、组件的生命周期
组件的声明周期主要分为3个状态:
Mounting:这个是已经插入的DOM
Updating:正在被重新渲染
Unmounting:已移除的真实DOM
React 为每个状态都提供了两种处理函数,will函数在进入状态之前调用,did函数在进入状态之后调用,三种状态共计五种处理函数。
componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()
React 还提供两种特殊状态的处理函数。
componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
var Hello = React.createClass({
getInitialState: function () {
return {
opacity: 1.0
};
},
componentDidMount: function () {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
},
render: function () {
return (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
});
ReactDOM.render(
<Hello name="world"/>,
document.body
);
did函数在进入状态之后调用,我们在这里调用了 componentDidMount
的方法,也就是在hello组件加载之后,然后在 componentDidMount
方法中设置一个定时器,这个定时器的目的是每隔100毫秒,但是在透明图小于一定值的话,就会重新的渲染。
我们注意到我们对样式的定义是 style={{opacity: this.state.opacity}}
,而不是style="opacity:{this.state.opacity};"
,因为react的组件的样式实质上是一个对象,所以第一重大括号表示的是js的语法,第二重大括号表示的是对象。
Ajax
对于组件的数据来源,通常是通过Ajax请求在服务器获取的,我们依旧可以通过componentDidMount
方法来设置Ajax请求,等到请求成功的时候,再使用this.setState
方法重新渲染UI。
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
},
render: function() {
return (
<div>
{this.state.username}'s last gist is
<a href={this.state.lastGistUrl}>here</a>.
</div>
);
}
});
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
document.body
);
上面的代码的实现是使用 jquery
来完成 Ajax
的请求,这也就很方便的说明了。react本身是没有任何的依赖的,我们可以不使用 jquery
而是使用其他的库。
我们现在比较经常使用的是使用 promise
来进行操作,而且我们通过查看promise的状态来返回了不同的信息。
var RepoList = React.createClass({
getInitialState: function() {
return { loading: true, error: null, data: null};
},
componentDidMount() {
this.props.promise.then(
value => this.setState({loading: false, data: value}),
error => this.setState({loading: false, error: error}));
},
render: function() {
if (this.state.loading) {
return <span>Loading...</span>;
}
else if (this.state.error !== null) {
return <span>Error: {this.state.error.message}</span>;
}
else {
var repos = this.state.data.items;
var repoList = repos.map(function (repo) {
return (
<li>
<a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}
</li>
);
});
return (
<main>
<h1>Most Popular JavaScript Projects in Github</h1>
<ol>{repoList}</ol>
</main>
);
}
}
});