Python全栈开发学习--Reactjs--基础语法(中)--Day13
文章目录
1、state & props
2、组件生命周期
3、事件处理
4、条件渲染
一、state & props
上一篇文章中,学习了React的组件化,但是没有仔细研究props,特意将其放到这里跟state一起学习,因为它两很像而且容易混淆。
function Hello(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Hello name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
回顾之前学习的组件的概念。上述过程中:
1、我们先是封装了一个名为Hello
的组件,里面包含了<h1>
这个子组件
2、自定义组件<Hello >
,const element = <Hello name="Sara" />
这一句我理解为类似于一个函数调用实例化的过程
3、使用render()
渲染这个组件
这里面我们分析一下porps
,在这个组件渲染的过程中,我们发现Hello组件会接收一个传递进来的参数,最后我们在页面上打印的内容:Hello { name },这个name是一个变量,是我们调用函数实例化的时候外部传进来的参数。
到这里,我们或许就能理解React组件化,复用的一些设计思想。即当我们想在网页中显示Hello Lisa、Hello Jone、Hello James等等等等,我们就不在需要去写这么多h1
标签,直接调用我们的Hello组件,并且将 name 参数传递进组件当中就可以了。
下面再看看class声明的组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
const element = <Welcome name="Lisa"/>;
ReactDOM.render(
element,
document.getElementById('root')
);
我们发现,class中获取参数的时候,需要使用this
关键字去获取到我们的参数{name:"Lisa"}
自定义组件格式: <组件名 参数名=参数值>
1、props
1.1 规则
(1)只读性:
props经常被用作渲染组件和初始化状态,当一个组件被实例化之后,它的props是只读的,不可改变的。如果props在渲染过程中可以被改变,会导致这个组件显示的形态变得不可预测。只有通过父组件重新渲染的方式才可以把新的props传入组件中。
(2)数据流方向
参数通过父组件传递到子组件中
1.2 默认值
我们可以通过组件类的 defaultProps 属性为 props 设置默认值
class HelloMessage extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
HelloMessage.defaultProps = {
name: 'Lisa'
};
const element = <HelloMessage/>;
ReactDOM.render(
element,
document.getElementById('example')
);
2、state
React 把组件看成是一个状态机。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面即可。总而言之,它是一种动态变化的状态,它改变就会重新渲染组件的UI界面。
2.1 规则
可以改变
state是可以被改变的。不过,不可以直接通过this.state=
的方式来修改,而需要通过this.setState()
方法来修改state
。setState()
采用merge的方式修改state,会重新调用render()刷新UI,直接通过this.state=‘xxx’的方式也会修改state但是不会重新渲染。
2.2 this.setState()
使用这个方法:改变state的状态后,重新渲染组件。类似于一个刷新功能
2.3 例子
接下来通过一个 定时器组件 例子来掌握它
(1)声明 定时器 组件
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在时间: {这里将显示具体时间}.</h2>
</div>
);
}
}
(2)参数传递:我们需要将实时的时间传递进来,使用props?设想一下,假如我们使用props传递外部参数进来,我们的流程就是每次在外部获取到当前时间后,再调用组件传参,再渲染,这个过程中,我们无疑多了很多不必要的调用组件传参的步骤。我们的想法应该是在组件内部自己实时刷新当前时间,这样我们只需调用一次组件即可。
(3)我们使用 this.state
使用方法:
1、添加一个类构造函数来初始化状态 this.state,
2、类组件应始终使用 props 调用基础构造函数。
3、
constructor(props) {
super(props);
this.state = xxxx;
}
class Clock extends React.Component {
/*
构造函数初始化state
Class 组件应该始终使用 props 参数来调用父类的构造函数。
*/
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>
);
}
}
/* 无需再传入参数,参数将在组件内部实时更新
移除 属性
*/
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
(4)至此,我们渲染这个组件的时候,会显示出当前时间,然而,它并没有进行实时刷新。下面我们需要涉及到一个组件生命周期的内容(组件生命周期)
我们将通过组件的生命周期函数来进行实时刷新
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
/ * 当组件渲染完毕的时候执行 */
componentDidMount() {
}
/ * 当组件卸载的时候执行*/
componentWillUnmount() {
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
(5)设置计时器
/* 当组件渲染完成时,设置计时器,该计时器函数会每隔1秒执行 tick 函数*/
componentDidMount() {
this.timerID = setInterval(
/* 箭头函数 */
() => this.tick(),
1000
);
}
/* 该函数会刷新state的值为当前时间,并且执行 setState()方法 会对组件重新渲染*/
tick() {
this.setState({
date: new Date()
});
}
(6)销毁计时器
/* 当组件被删除的时候,将计时器也删除*/
componentWillUnmount() {
clearInterval(this.timerID);
}
(7)完整计时器组件
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')
);
3、props、state总结
大部分组件的工作应该是从 props
里取数据并渲染出来。但是,有时需要对用户输入、服务器请求或者时间变化等作出响应,这时才需要使用 state
。
尝试把尽可能多的组件无状态化。 这样做能隔离 state,把它放到最合理的地方,也能减少冗余并,同时易于解释程序运作过程。
3.1、state
state的主要作用是用于组件保存、控制以及修改自己的状态,它只能在constructor中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新渲染。
3.2、props
props理解为从外部传入组件内部的数据。由于React是单向数据流,所以props基本上也就是从服父级组件向子组件传递的数据。组件之间的通信
3.3、区别
(1)state
是组件自己管理数据,控制自己的状态,可变;
(2)props
是外部传入的数据参数,不可变;
二、组件生命周期
组件的生命周期讲的是组件从创建到移除的一系列过程。
1、三个状态
组件的生命周期有三个状态:
(1)Mount
:插入真实 DOM(初始化阶段)
(2)Update
:被重新渲染(更新阶段)
(3)Unmount
:被移出真实 DOM(销毁阶段)
2、过程函数
2.1 初始化阶段
这一阶段包括组件的创建、实例化调用、完成渲染插入。
(1)componentWillMount()
组件初始化时调用,在整个生命周期中只调用一次;
(2)render()
是组件在创建虚拟dom,进行diff算法,更新dom树
(3)componentDidMount()
是组件渲染结束之后调用。
2.2 更新阶段
当组件的属性或者状态改变时会重新渲染
(1)shouldComponentUpdate()
是组件接受新的state或者props时调用,这是一个对性能优化非常重要的一个函数
(2)componentWillUpdata()
是在组件将要更新时才调用,可以修改state值
(3)render()
是组件执行渲染
(4)componentDidUpdate()
是组件更新完成后调用,此时可以获取dom节点。
2.3 销毁阶段
当一个组件被移出Dom树时,组件就会被卸载
(1)componentWillUnmount()
是组件将要卸载时调用,一些事件监听和定时器需要在此时清除。
三、事件处理
React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同
3.1
(1)点击触发函数HTML 通常写法是:
<button onclick="activateLasers()">
**按钮
</button>
(2)点击触发函数React 中写法为:
<button onClick={activateLasers}>
**按钮
</button>
3.2
在React当中,return false不会阻止事件的默认行为,需要调用 e.preventDefault()
;
通常我们在 HTML 中阻止链接默认打开一个新页面,可以这样写:
<a href="#" onclick="console.log('点击链接'); return false">
点我
</a>
而在React中要这样写:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('链接被点击');
}
return (
<a href="#" onClick={handleClick}>
点我
</a>
);
}
3.3 事件的this绑定
这一部分还是以具体的demo来学习,根据官方文档的demo,我们要实现一个按钮,按钮初始为开,点一次就开,再点一次又关。要实现这样一个开关按钮。
(1)语法一:在构造函数中bind(this)
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
大概意思就是,我们点击一次之后,下一次点击要想使用上一次点击时的this对象,就要在构造函数中进行一个绑定。
当然如果我们不想绑定那么可以使用一种叫做实验性语法?,大概就是实现上下文绑定的不同做法吧。
(2)语法二: handleClick = () => { 执行代码 }
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
}
handleClick = () => {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('example')
);
(3)语法3: <button onClick={() => this.handleClick()}>
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
}
handleClick(){
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={() => this.handleClick()}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('example')
);
此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
大概就是建议使用(1)(2)这两种语法吧。
3.4 、事件处理传参
1 、箭头函数
语法:<button onClick={(e) => this.clickMe(param, e)}>clickme</button>
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name:"Lisa",
age: 0
}
}
addOneClick(num,e){
e.preventDefault();
/* 更改年龄 */
this.setState({ age: this.state.age + num })
}
render() {
return <div>
/* 点击 触发函数addOneClick
传递num=2,e参数
*/
<a href="#" onClick={(e) => this.addOneClick(2, e)}>点我啊</a>
{this.state.name}今年{this.state.age}岁了!
</div>;
}
}
2、bind
通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面
语法:<button onClick={this.clickMe.bind(this, id)}> clickme</button>
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name:"Lisa",
age: 0
}
}
addOneClick(num,e){
e.preventDefault();
this.setState({ age: this.state.age + num })
}
render() {
return <div>
<a href="#" onClick={this.addOneClick.bind(this,2)}>点我啊</a>
{this.state.name}今年{this.state.age}岁了!
</div>;
}
}
四、条件渲染
条件渲染,顾名思义是根据一些条件,渲染不同的组件。最常见的例子就是用户登录与否显示不同的组件。
例如:
function UserGreeting(props) {
return <h1>欢迎您!用户:某某某</h1>;
}
function GuestGreeting(props) {
return <h1>请先注册。</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
/*判断用户是否登录条件*/
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<Greeting isLoggedIn={false} />,
document.getElementById('example')
);
4.1、不用if,用&&
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
在 JavaScript 中,true && expression
总是会返回 expression,false && expression
总是会返回 false。
因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
/*unreadMessages.length > 0成立就渲染h2,否则跳过 */
4.2 三目运算符
使用 JavaScript 中的三目运算符 condition ? true : false
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b>
(1)isLoggedIn为真:输出currently
(2)isLoggedIn为真:输出not
4.3 阻止条件渲染
在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。
下面的示例中,<WarningBanner />
会根据 prop 中 warn 的值来进行条件渲染。如果 warn 的值是 false,那么组件则不会渲染:
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
上一篇: Vue快速入门笔记
下一篇: HTML5离线应用与客户端存储的实现