1.上下文(Context)
react是單項資料流,資料是自上往下傳遞的。如果父元件想傳遞資料給孫元件,需要父傳遞到子,子再傳遞到孫。也可以用上下文解決。
在某些場景下,你想在整個元件樹中傳遞資料,但卻不想手動地在每一層傳遞屬性。你可以直接在 React 中使用強大的”context” API解決上述問題。
react路由就是通過上下文實現的。
上下文實現步驟
1. 在父元件裡定義 childContextTypes 子上下文型別
2. 在父元件裡還要定義一個getChildContext用來返回上下文物件
3. 在要接收這些上下文物件的元件裡寫義contextTypes
複製程式碼
實現一個小功能,有header,main兩個元件,分別包含title,content元件,公用一個顏色。
父元件
- childContextTypes
- getChildContext
子元件
- contextTypes
- this.context
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
/**
* 1. 在父元件裡定義 childContextTypes 子上下文型別
* 2. 在父元件裡還要定義一個getChildContext用來返回上下文物件
* 3. 在要接收這些上下文物件的元件裡寫義contextTypes
*/
export default class HomePage extends Component {
static childContextTypes = {
color: PropTypes.string,
setColor:PropTypes.func
}
getChildContext() {
return {
color: this.state.color,
setColor:this.setColor
}
}
//狀態不能別人改,只能自己改。
constructor() {
super();
this.state = { color: 'red' };
}
setColor = (color)=>{
this.setState({color});
}
render() {
return (
<div>
<Header />
<Main />
</div>
)
}
}
class Header extends Component {
render() {
return (
<div>
<Title />
</div>
)
}
}
class Main extends Component {
render() {
return (
<div>
<Content />
</div>
)
}
}
class Title extends Component {
static contextTypes = {
color: PropTypes.string
}
render() {
//this.context
return (
<div>
<h1 style={{ color: this.context.color }}>我是標題</h1>
</div>
)
}
}
class Content extends Component {
static contextTypes = {
color: PropTypes.string,
setColor: PropTypes.func
}
render() {
console.log(this.context);
return (
<div>
<h1 style={{ color: this.context.color }}>我是內容</h1>
<button onClick={()=>this.context.setColor('green')}>變綠</button>
<button onClick={()=>this.context.setColor('yellow')}>變黃</button>
</div>
)
}
}
複製程式碼
2.片段(fragments)
React 中一個常見模式是為一個元件返回多個元素。 片段(fragments) 可以讓你將子元素列表新增到一個分組中,並且不會在DOM中增加額外節點。
import React,{Component} from 'react';
class List extends Component{
render(){
return (
{ this.props.messages.map((item,key)=><li key={key}>{item}</li>)}
)
}
}
export default class Messages extends Component{
constructor(){
super();
this.state = {messages:[1,2,3]};
}
render(){
return (
<ul>
<List messages = {this.state.messages}/>
</ul>
)
}
}
複製程式碼
將li的邏輯單獨拎出來成立一個元件,但是這樣會報錯,react只能返回一個頂層元素,現在返回的是很多li。需要在他的外層新增一個頂層元素。
class List extends Component{
render(){
return (
<div>
{ this.props.messages.map((item,key)=><li key={key}>{item}</li>)}
</div>
)
}
}
複製程式碼
但是這樣ul中將會有div。這樣dom結構就亂了。
這時候就需要用到片段了:
class List extends Component{
render(){
return (
<React.Fragment>
{ this.props.messages.map((item,key)=><li key={key}>{item}</li>)}
</React.Fragment>
)
}
}
複製程式碼
<React.Fragment>標籤不會形成新的dom元素。完美解決這個問題。
另一種更簡單更形象寫法,但是現在還不支援:
class List extends Component{
render(){
return (
<>
{ this.props.messages.map((item,key)=><li key={key}>{item}</li>)}
</>
)
}
}
複製程式碼
3.插槽(Portals)
Portals 提供了一種很好的方法,將子節點渲染到父元件 DOM 層次結構之外的 DOM 節點。
ReactDOM.createPortal(child, container)
複製程式碼
- 第一個引數(child)是任何可渲染的 React 子元素,例如一個元素,字串或 片段(fragment)
- 第二個引數(container)則是一個 DOM 元素
需求:頁面中有很多元件,但每個元件都一個個彈窗功能。彈窗不應屬於任何一個元件。
在index.html中放入模態框的根節點
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<div id="modal-root" class="modal-root"></div>
</body>
</html>
複製程式碼
modal-root放入的為模態框元件,我們的其他元件在root中。
模態框元件
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import './Modal.css'
class Modal extends Component {
constructor() {
super();
this.container = document.querySelector('#modal-root');
}
render() {
return ReactDOM.createPortal(this.props.children, this.container);
}
}
export default class ModelPage extends Component {
constructor() {
super();
this.state = {show: false};
}
render() {
return (
<div>
<button onClick={() => this.setState({show: !this.state.show})}>顯示</button>
{
this.state.show ? <Modal>
<div className="modal-container">
<div className="modal-content">
<h1>顯示模態視窗</h1>
</div>
</div>
</Modal> : null
}
</div>
)
}
}
複製程式碼
4.錯誤邊界(Error Boundaries)
部分 UI 中的 JavaScript 錯誤不應該破壞整個應用程式。 為了解決 React 使用者的這個問題,React 16引入了一個 “錯誤邊界(Error Boundaries)” 的新概念。
import React,{Component} from 'react';
class ErrorBoundary extends Component{
constructor(){
super();
this.state = {hasError:false};
}
componentDidCatch(hasError){
this.setState({hasError});
}
render(){
if(this.state.hasError){
return <div>此元件暫時無法顯示</div>
}
return this.props.children
}
}
class Todo extends Component{
render(){
return <div>{null.toString()}</div>
}
}
export default class MyPage extends Component{
render(){
return (
<ErrorBoundary>
<Todo/>
</ErrorBoundary>
)
}
}
複製程式碼
null.toString()不合法,會顯示:此元件暫時無法顯示。