Python全栈开发学习--Reactjs--基础语法(中)--Day13

18 篇文章 0 订阅
16 篇文章 0 订阅

文章目录

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()方法来修改statesetState()采用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')
);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值