欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-06 19:13:25
...

文章目录

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')
);