---
# System prepended metadata

title: Module、Component
tags: [Javascript, React]

---

# 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
