React_2
目录:
组件和prop
定义组件
定义组件最简单的方式就是编写JavaScript函数
function HelloMessage(props) {
return <h1>Hello World!</h1>;
}
const element = <HelloMessage />;
ReactDOM.render(
element,
document.getElementById('example')
);
这里HelloMessage方法就是一个有效的React接收带有数据的props对象并返回一个React
当然也可以通过ES6的class来定义组件
class HelloMessage extends React.Component {
render() {
return <h1>Hello World!</h1>;
}
}
原生HTML元素名以小写字母开头,而自定义的React类名以大写字母开头,除此之外还需要注意组件类只能包含一个顶层标签,否则会报错。
如果需要向组件传递参数,就需要使用props传递参数
function HelloMessage(props) {
return <h1>Hello {props.name}!</h1>;
}
const element = <HelloMessage name="why"/>;
ReactDOM.render(
element,
document.getElementById('example')
);
整体渲染流程:
- 调用ReactDOM.render()函数,并传入element
- React调用HelloMessage组件,并将{name: 'why'}作为props传入
- HelloMessage组件将
<h1>Hello why!</h1>
元素作为返回值 - ReactDOM将DOM更新为
<h1>Hello why!</h1>
复合组件
组件可以在输出中引用其他组件
function Name(props) {
return <h1>网站名称:{props.name}</h1>;
}
function Url(props) {
return <h1>网站地址:{props.url}</h1>;
}
function App() {
return (
<div>
<Name name="why's blog" />
<Url url="http://www.whysdomain.com" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('example')
);
提取组件
示例一个组件
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
这里接收author对象、text和data,可以提取一个Avatar组件
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
这里使用的user,和外边传递的author不一样,就需要进行一下赋值
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
props只读性
组件无论是使用函数声明还是通过class声明,都决不能修改自身的props
State和生命周期
通过定时器方式
之前时钟例子
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
可以先将时钟变为Clock组件,还是设置计时器每秒更新一次
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
但是还是需要外部的计时器,想实现的功能为
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
就需要state,与prop不通的是,state是私有的
state使用
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
向组件中添加state
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
声明周期使用
然后再在生命周期上进行数据更新
- 当组件第一次被渲染到DOM的时候,就可以设置计时器,这个过程为mount,对应方法componentDidMount
- 当组件被删除的时候的操作,为umount,对应方法为componentWillUnmount
可以在渲染的时候,在class中添加定时器的数据流
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
定时器调用的方法为
tick() {
this.setState({
date: new Date()
});
}
在删除的时候清除
componentWillUnmount() {
clearInterval(this.timerID);
}
整体就是
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
整体流程为
<Clock />
被传递给ReactDOM.render()
的时候,React会调用Clock组件的构造函数- React根据render()转为React的DOM
- DOM插入到浏览器上的时候,会调用ComponentDidMount()的生命周期方法,会向浏览器请求设置一个计时器来调用tick方法
- 浏览器每秒调用一次tick()方法,通过setState()更新数据,React发现数据变化后会重新确定DOM进行渲染输出
- 一旦Clock组件从DOM移除,就会调用componentWillUnmount()停止设置的计时器
state触发react更新的情况
首先
this.state.comment = 'Hello';
只会进行赋值,不会触发渲染组件this.setState({comment: 'Hello'});
会触发渲染组件
this.setState({
counter: this.state.counter + this.props.increment,
});
以上可能无法更新计数器,因为this.props和this.state可能会异步更新,解决方式是接收一个函数而不是对象
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
或
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
数据是自上向下流动
function FormattedDate(props) {
return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);