# 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元素)。

### 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>
)
}
}
```

這樣可複用性非常強,我們可以把組件的內容封裝好,然後靈活在使用在任何組件內。另外這裡要注意的是,**自定義的組件都必須要用大寫字母開頭**,**普通的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 => 最後一起呈現到畫面中

## 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>
)
}
}
```

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>
)
}
}
```

或是這樣
```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中非常常用。==