React學習筆記之react基礎篇(2)
上一節我已經對React中基本的組件操作進行了說明,這一節我將對組件的一些附加屬性(如:組件的生命周期和組件的樣式)以及一些其他功能進行講解
一.組件的樣式
1.外部CSS樣式表:
//style.css
.foo{
width:100%;
height:50px;
}
//引入的兩種方式
1.在使用組件的HTML頁面中通過標簽引入
<link rel="stylesheet" type="text/css" href="style.css">
2.在組件中添加入該模塊
import ‘./style.css‘;
說明:使用css樣式表經常遇到的問題就是clsss名稱沖突,業內解決這個問題的一個常用方案就是使用 CSS Modelus。
2.內聯樣式
function Welcome(props){ return( <h1 style={{ //第一個括號表示JavaScript表達式,第二個括號表示這個JavaScript表達式是一個對象 width:"100%", height:"50px", backgroundColor:"blue" //必須使用駝峰命名法 }} > Hello,{props.name} </h1> ); }
二.組件生命周期
通常,組件的生命周期可以分成三個階段:掛載階段丶更新階段丶卸載階段
1.掛載階段
這個階段組件被創建,執行初始化,並被掛載到DOM中,完成組件的第一次渲染。
1.constructor
組件被創建時,會首先調用組件的構造方法,通常用於初始化組件的state以及綁定事件處理方法等工作
constructor(props,context) {
super(props,context)
console.log(this.props,this.context) // 在內部可以使用props和context
}
當然如果你只需要在構造函數內使用props或者context,那麽只傳入一個參數即可,如果都不可以,就都不傳。
2.componentWillMount
這個方法在組件被掛載到DOM前調用,而且只會被調用一次,很少使用,因為它能完成的工作都可以讓constructor去完成。
3.render
這是定義組件唯一必要的方法(組件的其他生命周期方法都可以不寫)。render函數會插入JSX生成的dom結構,react會生成一份虛擬dom樹,在每一次組件更新時,在此react會通過其diff算法比較更新前後的新舊DOM樹,比較以後,找到最小的有差異的DOM節點,並重新渲染。同時在render中不能調用this.setState,因為這會改變組件的狀態。
4.componentDidMount
這個方法在組件掛載到DOM後進行調用,而且只會被調用一次。這個方法通常用於向服務器請求數據,在這個方法中調用this.setState會引起組件的重新渲染
2.更新階段
組件被掛載到DOM後,組件的props或state可以引起組件更新。props由父組件的render方法被調用引起,state通過調用this.setState修改組件的state來進行觸發的。
1.componentWillReceiveProps(nextProps)
通過對比nextProps和this.props,將nextProps setState為當前組件的state,從而重新渲染組件
2.shouldComponentUpdate(nextProps,nextState)
通過比較nextPorps丶nextState和組件當前的props丶state決定這個方法的返回結果。若返回true則繼續渲染,否則後續的componentWillUpdate丶coponentDidUpdate方法不會進行調用,可以減少不必要的渲染。
3.componentWillUpdate(nextProps,nextState)
這個方法在組件render調用前執行,可以作為組件更新發生前執行某些工作的地方,同時該方法不能調用setState。
4.coponentDidUpdate(prevProps,prevState)
這個方法在組件更新後進行調用,可以作為操作更新後DOM的方法,同時該方法不能調用setState.
3.卸載階段
組件從DOM中被卸載的過程,這個過程中只有一個生命周期方法:
1.componentWillUnmount()
在組件被清理前進行調用,可以執行一些清理操作。
三.列表和Keys
React示例:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((numbers) =>
<li>{numbers}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById(‘example‘)
);
我們可以將以上實例重構成一個組件,組件接收數組參數,每個列表元素分配一個 key,不然會出現警告 a key should be provided for list items,意思就是需要包含 key:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById(‘example‘)
);
四.表單
HTML 表單元素與 React 中的其他 DOM 元素有所不同,因為表單元素生來就保留一些內部狀態。在 HTML 當中,像<input>, <textarea>, 和 <select> 這類表單元素會維持自身狀態,並根據用戶輸入進行更新。但在React中,可變的狀態通常保存在組件的狀態屬性中,並且只能用 setState() 方法進行更新。為了讓表單元素的狀態的變更也能通過組件的state管理,React采用受控組件的技術達到這一目的。
1.受控組件
如果一個表單元素的值是由React進行管理的,那它就是一個受控組件
示例:
class HelloMessage extends React.Component { constructor(props) { super(props); this.state = {value: ‘Hello Runoob!‘}; //對數據進行初始化 this.handleChange = this.handleChange.bind(this); } handleChange(event) { //對數據進行更新 this.setState({value: event.target.value}); } render() { var value = this.state.value; return <div> <input type="text" value={value} onChange={this.handleChange} /> <h4>{value}</h4> </div>; } } ReactDOM.render( <HelloMessage />, document.getElementById(‘example‘) );
示例二:
在以下實例中我們將為大家演示如何在子組件上使用表單。 onChange 方法將觸發 state 的更新並將更新的值傳遞到子組件的輸入框的 value 上來重新渲染界面。你需要在父組件通過創建事件句柄 (handleChange) ,並作為 prop (updateStateProp) 傳遞到你的子組件上。
class Content extends React.Component { render() { return <div> <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} /> <h4>{this.props.myDataProp}</h4> </div>; } } class HelloMessage extends React.Component { constructor(props) { super(props); this.state = {value: ‘Hello Runoob!‘}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } render() { var value = this.state.value; return <div> <Content myDataProp = {value} updateStateProp = {this.handleChange}></Content> </div>; } } ReactDOM.render( <HelloMessage />, document.getElementById(‘example‘) );
2.非受控組件
非受控組件是指表單元素的狀態依然由表單元素自己管理,而不是交給React組件管理。使用非受控組件需要由一種方式可以獲取到表單元素的值,React中提供了一個特殊的屬性ref,用來引用React組件或DOM元素的實例,因此我們可以通過為表單元素定義ref屬性獲取元素的值。
示例:
class Simple extends Component{
constructor(props){
super(props);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleSubmit(event){
//通過this.input獲取到input元素的值
alert(‘this.input.value‘);
event.preventDefault(); //preventDefault() 方法阻止元素發生默認的行為,即阻止表單提交
}
render(){
return(
<form onSubmit = {this.handleSubmit)>
<label>
title:
{/*this.input指向當前input元素*/}
<input type="text" defaultValue="something" ref={(input)=>this.input=input}/> //通過defaultValue為元素設置默認值,同時通過ref函數接收函數作為參數進行數據引用。
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
五.React16新的返回值類型
React16之前,render方法必須返回單個元素。現在render方法支持兩種新的返回類型:數組和字符串
示例(數組):
class ListComponent extends Component{
render(){
return [
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>
];
}
}
示例(字符串):
class StringComponent extends Component{
render(){
return "Just a Strings";
}
}
六.錯誤處理
組件在運行期間如果執行出錯就會阻塞整個頁面的渲染,這時候只能刷新頁面才能回復應用。react16提供了一種友好的錯誤處理方式-----錯誤邊界。錯誤邊界是能夠捕捉子組件的錯誤並對其進行優雅處理的組件。優雅處理可以是輸出錯誤日誌丶顯示出錯提示等。
定義componentDidCatch(error,info)這個方法的組件將成為一個錯誤邊界。
示例:
import React, { Component } from "react";
const Profile = ({ user }) => <div>name: {user.name}</div>;
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(err, info) {
// 顯示錯誤UI
this.setState({ hasError: true });
// 同時輸出錯誤日誌
console.log(err, info);
}
render() {
if (this.state.hasError) {
return <h1>Oops, something went wrong!</h1>;
}
return this.props.children;
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
user: { name: "react" }
};
}
// 將user置為null,模擬異常
onClick = () => {
this.setState({ user: null });
};
render() {
return (
<div>
<ErrorBoundary>
<Profile user={this.state.user} />
</ErrorBoundary>
<button onClick={this.onClick}>更新</button>
</div>
);
}
}
export default App;
點擊按鈕之後因為user為null,所以程序就會拋出異常並且被ErrorBoundary捕捉並在界面上顯示錯誤提示信息。
七.Portals定義全局配置
React16的Portals特性讓我們可以把組件渲染到當前組件樹以外的DOM節點上,這個特性典型的應用場景是渲染應用的全局彈框
Portals的實現依賴ReactDOM的一個新的API:
ReactDOM.createPortal(可以被渲染的節點,需要掛載的節點)
示例:
import React, { Component } from "react";
import ReactDOM from "react-dom"; //因為需要使用ReactDOM中的API所以需要引入對應的庫
class Modal extends Component {
constructor(props) {
super(props);
this.container = document.createElement("div"); //創建一個div節點
document.body.appendChild(this.container); //將div節點綁定到根節點下面
}
componentWillUnmount() { //從根節點移除div節點
document.body.removeChild(this.container);
}
render() {
return ReactDOM.createPortal( //創建的DOM樹掛載到this.container指向的div節點下面
<div className="modal">
<span className="close" onClick={this.props.onClose}>
×
</span>
<div className="content">{this.props.children}</div>
</div>,
this.container
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = { showModal: true };
}
closeModal = () => {
this.setState({ showModal: false });
};
render() {
return (
<div>
<h2>Dashboard</h2>
{this.state.showModal && (
<Modal onClose={this.closeModal}>Modal Dialog </Modal>
)}
</div>
);
}
}
export default App;
八.自定義DOM屬性
react16之前會忽略不識別的HTML屬性,現在react會把不識別的屬性傳遞給DOM元素作為一個新的屬性。
示例:
<div custom="one" />
在瀏覽器中渲染出的DOM節點為:
<div />
而react16渲染出的DOM節點為:
<div custom="one" />
React學習筆記之react基礎篇(2)