---
# System prepended metadata

title: React 初探和事件點擊
tags: [React]

---

# React 初探和事件點擊

###### tags: `React`

### 初探

預備知識: 
1. 需要import/export檔案, 這需要有webpack基礎
2. 物件導向概念. 
3. JSX
4. ES6

其他概念:
1. virtual dom 
2. immutable data
3. state/props 

資料和顯示內容不一定是同步的, 現在react就是不用管UI惹, 我直接變更資料, 那UI就也會跟著資料而改變.


### client side render vs server side render (csr && ssr)

當右鍵檢視原始碼的時候, 發現什麼東西都沒有, 但是為什麼會有內容?
原因就是透過javaScript把東西產生出來. 這就是client-side-render

但如果是ssr, 我收到的response, 本來就有那些東西存在, 所以還是可以看到程式碼.



## Component是什麼?

在react裡面每一個東西都是一個component.
每一個component都要有render function 和 return. 
和切版沒兩樣, 只是要怎麼切. 有點像是拼積木這樣. 

> React.js中一切皆組件，用React.js寫的其實就是React.js組件。我們在編寫React.js組件的時候，一般都需要繼承React.js的Component。

> 一個組件類必須要實現一個render方法，這個render方法必須要返回一個JSX元素。

> 但這裡要注意的是，必須要用一個外層的JSX元素把所有內容包裹起來。返回並列多個JSX元素是不合法的，下面是錯誤的做法：

```javascript=
//這是錯誤的
render () {
  return (
    <div>第一个</div>
    <div>第二个</div>
  )
}
...
```
必須要用一個外層元素把內容進行包裹：

```javascript=
...
render () {
  return (
    <div>
      <div>第一个</div>
      <div>第二个</div>
    </div>
  )
}
...
```

那會把component放在哪?

> ans: `src`

`src`裡面先去找component. (ex:`App.js`)

```javascript=
import React from 'react'

//這是他的基本長相,一個叫做App的class,繼承了React提供的Component,裡面有render function, return畫面上所呈現的東西
class App extends React.Component {
    render(){
        return (
            <h1>Hello React frameWork</h1>
        )
    }
}
export default App
```

---
那有了component後, 我要把它引入到哪?

> ans: `index.js` (ex:`main.js`)

> 我們文件頭部從react的包當中引入了`React`狀語從句：`React.js的組件父類Component`。記住，只要你要寫React.js組件，那麼就必須要引入這兩個東西。

```javascript=
import React from 'react';
import ReactDom from 'react-dom';
import App from './App';

//這段話的意思是, 要把App這個component, render到這個dom物件上.
ReactDom.render(<App/>, document.getElementById('root'))
```

> `ReactDOM.render`功能就是把組件渲染和構造DOM樹，然後插入到頁面上某個特定的元素上（在這裡是id為`App`的div元素）。

![image alt](http://huzidaha.github.io/static/assets/img/posts/44B5EC06-EAEB-4BA2-B3DC-325703E4BA45.png)

### 1. 可以自行增加很多component. 

也可以這樣寫,我要Title這個componet再App裡面做渲染

==呼叫Component的話, 就像`<br/>`要有自己的標籤==

> 一個組件繼承Component類，有一個render方法，並且把這個組件的HTML結構返回

```javascript=
class Title extends React.Component{
    render(){
        return(
            <h1>Hello React</h1>
        )
    }
}

class App extends React.Component {
    render() {
        return (
            <div>
			   <Title />
               <Title />
			   <Title />
            </div>
        )
    }
}
```
![](https://i.imgur.com/VyLEq7Q.png)


這樣可複用性非常強，我們可以把組件的內容封裝好，然後靈活在使用在任何組件內。另外這裡要注意的是，**自定義的組件都必須要用大寫字母開頭**，**普通的HTML標籤都用小寫字母開頭**。


在App.js中

### 2. 在component裡面可以再增加component. 

```javascript=
class Text extends React.Component{
    render(){
        return(
            <p>text</p>
        )
    }
}


class App extends React.Component {
    render() {
        return (
            <div>
               <Title />
               <Text />
            </div>
        )
    }
}

class Title extends React.Component{
    render(){
        return(
            <h1>Hello React</h1>
        )
    }
}
```

背後的流程是,先call App => 碰到`<Title />` => render => 碰到`<Text />` => render => 最後一起呈現到畫面中

![](https://i.imgur.com/eLccQ9Y.png)


## JSX 

看起來像是純HTML代碼寫在JavaScript代碼裡面。你也許會說，這不就有語法錯誤了麼？這完全不是合法的JavaScript代碼。這種看起來“在JavaScript寫的標籤的”語法叫JSX。

### 表達式插入

> 大括號就是代表裡面可以放javaScript的程式碼, 所以可以放動態的東西, 有點像是php的 `<?.....?>`

在JSX當中你可以插入JavaScript的表達式，表達式返回的結果會相應地渲染到頁面上。表達式用`{}`包裹。例如：

```javascript=
...
render () {
  const word = 'is good'
  return (
    <div>
      <h1>React 小书 {word}</h1>
    </div>
  )
}
...
```
頁面上就顯示“React小書is good”

也可以把它寫成一個函數表達式返回：

```javascript=
...
render () {
  return (
    <div>
      <h1>React 小书 {(function () { return 'is good'})()}</h1>
    </div>
  )
}
...
```
簡而言之，`{}`內可以放任何JavaScript的代碼，包括變量、表達式計算、函數執行等等。render會把這些代碼返回的內容如實地渲染到頁面上，非常的靈活。

---
表達式插入不僅僅可以用在標籤內部，也可以用在標籤的屬性上，例如：

```javascript=
class App extends React.Component {
    render(){
        const name = 'yoyoyoyo'
        return (
            <div className ={name + '123'}>
                <Title />
                <Text />
            </div>
        )
    }
}
```
![](https://i.imgur.com/5gWXAPC.png)

1. 在`react`裡面如果要用`class`, 要改為`className`, 因為原本的class被拿去做其他用途

2. 還有一個特例就是for屬性，例如`<label for='male'>Male</label>`，因為`for`也是JavaScript的關鍵字，所以在JSX用`htmlFor`替代，即`<label htmlFor='male'>Male</label>`。

而其他的HTML屬性例如`style`、`data-*`等就可以像普通的HTML屬性那樣直接添加上去。

---

### 條件返回

實際上，我們可以在render函數內部根據不同條件返回不同的JSX。

```javascript=
class App extends React.Component{
    render(){
        const isGoodword = false
        return(
            <div>
               <h1>
                   React小書
                   {isGoodword ? <strong> is good</strong> : <span> is not good</span>}
               </h1>
            </div>
        )
    }
}
```
把isGoodWord改成false然後再看頁面上就會顯示React 小书 is not good。

如果你在表達式插入裡面返回`null`，那麼React.js會什麼都不顯示，相當於忽略了該表達式插入。結合條件返回的話，==我們就做到顯示或者隱藏某些元素：==

```javascript=
class App extends React.Component{
    render(){
        const isGoodword = false
        return(
            <div>
               <h1>
                   React小書
                   {isGoodword ? <strong> is good</strong>: null}
               </h1>
            </div>
        )
    }
}
```
條件返回JSX 的方式在React.js 中很常見，組件的呈現方式隨著數據的變化而不一樣，你可以利用JSX 這種靈活的方式隨時組合構建不同的頁面結構。

---

## JSX 元素變量

```javascript=
class App extends React.Component{
    render(){
        const isGoodword = true
        const goodWord = <strong> is good</strong>
        const badWord = <span> is not good</span>
        return(
            <div>
               <h1>
                   React小書
                   {isGoodword ? goodWord: badWord}
               </h1>
            </div>
        )
    }
}

```

這裡給把兩個JSX元素賦值給了`goodWord`和`badWord`兩個變量，然後把它們作為表達式插入的條件返回值。達到效果和上面的例子一樣，隨機返回不同的頁面效果呈現。

再舉一個例子：(這例子無法正常render)

```javascript=
class App extends React.Component{
    rendergoodWord(goodWord, badWord){
        const isGoodword = true
        isGoodword ? goodWord: badWord
    }

    render(){
        return(
            <div>
               <h1>
                   React小書
                   {this.rendergoodWord(
                       <strong> is good</strong>, <span> is not good</span>
                   )}
               </h1>
            </div>
        )
    }
}

```
這裡我們定義了一個`renderGoodWord`函數，這個函數接受兩個JSX元素作為參數，並且隨機返回其中一個。在render方法中，我們把上面例子的兩個JSX元素傳入`renderGoodWord`當中，通過表達式插入把該函數返回的JSX元素插入到頁面上。


---

==注意==

要被`return`的東西, 和`return`是不同行的, 要用`()` 先包住,否則會`return undefined`


## 在react裡面加上eventListener


> 在React.js裡面監聽事件是很容易的事情，你只需要給需要監聽事件的元素加上屬性類似於`onClick`、`onKeyDown`這樣的屬性，例如我們現在要給`Title`加上點擊的事件監聽：

==重點==

我要在哪裡宣告? 
- 如果是render前面, 那我底下就要調用成`onClick={this.sayhi}`
- 如果是在render後面, 那我底下就直接調用成`onClick = {sayhi}`

為什麼會有這兩者的區別?

主要和物件導向的調用有關,因為在前者, 不知道你要調用哪一個`sayhi`, 所以特別指名我要這個instance裡面的sayhi
而後者, 因為已經在render裡面了, 所以我調用可以直接找到上層, 這邊也和作用域有關.

```javascript=
class Title extends React.Component {
    render(){
        const t = 'hello'
        function sayHi(){
            alert('hi')
        }
        return (
            <h1 onClick={sayHi}>{t}</h1>
        )
    }
}

class App extends React.Component {
    render() {
        const name = 'yoyoyo'
        return (
            <div className={name + 123} style={{
                color: 'red',
                fontSize : '40px'
            }}>
            <Title />
            </div>
        )
    }
}
```
![](https://i.imgur.com/0Pd0HXd.png)

或是這樣
```javascript=
class Title extends React.Component {
    sayHi() {
        alert('hi')
    }

    render(){
        const t = 'hello'

        return (
            <h1 onClick={this.sayHi}>{t}</h1>
        )
    }
}

class App extends React.Component {
    render() {
        const name = 'yoyoyo'
        return (
            <div className={name + 123} style={{
                color: 'red',
                fontSize : '40px'
            }}>
            <Title />
            </div>
        )
    }
}
```
> 在React.js不需要手動調用瀏覽器原生的`addEventListener`進行事件監聽。React.js幫我們封裝好了一系列的`on*`的屬性，當你需要為某個元素監聽某個事件的時候，只需要簡單地給它加上`on*`就可以了

> 沒有經過特殊處理的話，這些**on的事件監聽只能用在普通的HTML的標籤上**，而不能用在組件標籤上。

> 也就是說，`<Header onClick={…} />`這樣的寫法不會有什麼效果的。這一點要注意，但是有辦法可以做到這樣的綁定，以後我們會提及。現在只要記住一點就可以了：**這些on*的事件監聽只能用在普通的HTML的標籤上，而不能用在組件標籤上。**

==大多數的時候採用匿名函式,底下是es6的寫法==
```javascript=
class Title extends React.Component {
    render(){
        const t = 'hello'
        return (
            <h1 onClick={()=>{
                alert('hi!!!')
            }}>{t}</h1>
        )
    }
}

class App extends React.Component {
    render() {
        const name = 'yoyoyo'
        return (
            <div className={name + 123} style={{
                color: 'red',
                fontSize : '40px'
            }}>
            <Title />
            </div>
        )
    }
}
```

## event 

> 和普通瀏覽器一樣，事件監聽函數會被自動傳入一個`event` 對象，這個對象和普通的瀏覽器`event`對象所包含的方法和屬性都基本一致。不同的是React.js中的`event`對象並不是瀏覽器提供的，而是它自己內部所構建的.


> React.js將瀏覽器原生的event對象封裝了一下，對外提供統一的API和屬性，這樣你就不用考慮不同瀏覽器的兼容性問題。這個event對像是符合W3C標準（W3C UI Events）的，它具有類似於event.stopPropagation、event.preventDefault這種常用的方法。

```javascript=
class Title extends React.Component {
    sayHi(e) {
        alert(e.target.innerHTML)
    }

    render(){
        const t = 'React'

        return (
            <h1 onClick={this.sayHi}>{t}</h1>
        )
    }
}

class App extends React.Component {
    render() {
        const name = 'yoyoyo'
        return (
            <div>
            <Title />
            </div>
        )
    }
}
```

### 關於事件中的this

> 一般在某個類的實例方法裡面的this指的是這個實例本身。但是你在上面的handleClickOnTitle中把this打印出來，你會看到this是null或者undefined。



==this的值當下是看不出來的,取決於這個function怎麼被call==

### 延伸
```javascript=
class test {
    setName(name){
        this.name = name
    }
    say(){
        console.log(this)
    }
}
```
```javascript=
const t  = new test()
t.say() // test{}

t.setName('peter') // test{name: peter}
const funA = t.say
funA() // 等同於t.say => undefined
```

同理, 我在底下用一個function包住. 這樣會不知道原本的this是什麼? 

> 這是因為React.js調用你所傳給它的方法的時候，並不是通過對象方法的方式調用`（this.sayhi）`，而是直接通過函數調用`（sayhi）`，所以事件監聽函數內並不能通過this獲取到實例。

因此這邊要用`bind`綁定 


==重點==

在原本的Class中的`constructor`要增加這一行
綁定這個`this`等於這個`instance`,就是這個component本身

```javascript=
constructor(){
    super()
    this.state = {
        title:'hello',
        counter: 1
    }
    this.handleClick = this.handleClick.bind(this)
}	
```

== React.js的事件監聽方法需要手動bind到當前實例，這種模式在React.js中非常常用。==