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()方法來修改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')
);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章