# Module、Component ###### tags: `React` `Javascript` **1.Module 模組** 將可共用的js拆分成個別的檔案 **2.Component元件** 將個別布局的html/css/js/image拆分出來的集合, 兩者的目的都是重用、簡化、提高效率,但是規模不同 React 提供兩種Component 1. 函數式(簡單 <script type="text/babel"> function Demo(){//必須為大寫開頭 console.log(this);//babel編譯時會套上嚴格模式而此模式禁止自定義函數內this指向window因此這邊為undefined return <h1>test</h1>;//必須有傳回值 } ReactDom.render("<Demo/>",domcument.getElementById(''test));//使用標籤綁定到component /* 執行步驟 1. react 解析組件標籤後找到Demo 2. 調用Demo將回傳的虛擬Dom轉為真實Dom */ </script> 2. 類式(複雜 class Demo extends React.Component { //必須繼承React.Component render(){ //必須定義render 且回傳 //render中的this代表的會是Demo的實例對象 return ( <h1>test</h1> ) } } ReactDom.render("<Demo/>",domcument.getElementById(''test));//使用標籤綁定到component /* 執行步驟 1. react 解析組件標籤後找到Demo 2. 發現組件是使用類定義的,隨後new出來該類的實例,並且調用原型物件上的render方法 */ 簡單與複雜的差異為有狀態 組件"實例"的三大核心 1. state : - 透過建構子可在類別中初始化state 而此屬性必須為object - 狀態不可以直接更改,要用react api this.setState - 更新的動作為合併 merge - 改變狀態後會react會呼叫render 基本寫法 <script type="text/babel"> class Demo extends React.Component { constructor(props){ super(props) this.change= this.change.bind(this); this.state = { test : true } } render(){ return ( <button onClick={this.change}/> ) } change(){ const test = this.state.test; this.setState({test : !test }); } } </script> 簡化寫法 <script type="text/babel"> class Demo extends React.Component { state = { test : true } render(){ return ( <button onClick={this.change}/> ) } change = ()=>{ const test = this.state.test; this.setState({test : !test }); } } </script> 2. props - props是唯讀的 - 對傳入參數的限制建議寫在類中 - 參數由react自動傳入,但也可建立建構子傳入並丟給super來設定,如果建構子沒有接收傳入參數會導致在建構子中使用this.props獲取不到傳入參數 基本設定參數、使用參數語法 <script type="text/babel"> class Demo extends React.Component { render(){ const { name , age } = this.props; return ( <ul> <li>name : {name}</li> <li>age : {age}</li> </ul> ) } } //函數式組建可使用傳入參數,會將標籤屬性組成一個物件 //而傳入參數限制則只能定義在外側 function Demo2(props){ const {name,age} = props; return ( <ul> <li>name : {name}</li> <li>age : {age}</li> </ul> ) } //1.最基本寫法 要注意如果傳入值有形別則要用{}的方式型別才會正確 ReactDom.render('<Demo name="tom" age={10} / >' , document.getElementById('tag')); //2.基本寫法 const p = {name : "tom" , age :27} ReactDom.render('<Demo name={p.name} age={p.age} / >' , document.getElementById('tag')); //3.進階寫法 => 有條件限制:傳入的key同物件的key //這個寫法的 { ...p } 與es的展開運算符不同,雖然結果是展開物件,但這是babel與react特有的定義且只有在標籤中有效 ReactDom.render('<Demo {...p}/ >' , document.getElementById('tag')); </script> 對props進行限制( required 、 type .. 需要引入prop-types.js => PropTypes <script type="text/babel"> class Demo extends React.Component { //同下方的定義方式 static propsTypes = { name : PropTypes.string.isRequired, age : PropTypes.number, speak : PropTypes.func } static defaultProps= { age : 18 } state = { test : true } render(){ return ( <button onClick={this.change}/> ) } } //將會限制name為字串且必傳、age為數字、speak為函數 Demo.propsTypes = { name : PropTypes.string.isRequired, age : PropTypes.number, speak : PropTypes.func } //指定age預設為18 Demo.defaultProps= { age : 18 } function speak(){} ReactDom.render('<Demo name="tom" age={10} speak={speak}/ >' , document.getElementById('tag')); </script> 3. refs 1.字串型refs => 將dom保存進refs ps將廢棄 原因是效率問題 2.call back function => react呼叫回調函式時會傳入目前節點,透過箭頭函數的this特性將節點設定到實例上 當call back function直接寫在標籤上時會造成一個現象,畫面在初始化時只會調用一次但是在重新render時會呼叫兩次第一次會傳入null第二次會正確傳入dom 透過把函數綁定在類別內可解決這個問題 ps react在render時建立新的函數實例,所以react會有一個清空refs的動作 3.createRef : React提供建立一個容器來儲存ref該容器是專人專用 <script type="text/babel"> class Demo extends React.Component { input3 = React.createRef(); check = ()=>{ const {input1}= this.refs alert(input1.value) alert(input2.value) alert(input3.current.value) // 固定用法 } render(){ return ( <div> <input ref="input1" / > // 1.字串型 <input ref={ (node) => {this.input2 = node }} / > //2.call back function <input ref={this.input3} / > <button onClick={this.check}/> </div> ) } } </script> --- 在import時以資料夾區隔,若檔名為index時可以指定到資料夾而不用指定到js import Hello from './component/Hello';//實際上Hello內還有index.js