# 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中非常常用。==