目录
Context_作用
函数组件订阅Context
Fragments
错误边界_概念
错误边界_应用
Refs & DOM
Context_作用
React组件中数据是通过 props 属性自上而下(由父及子)进行传递的,但是有的时候中间的一些组件可能并不需要props的值。
//App.js
return (
<div>
<ContextTest />
</div>
);
//ContextTest.js
import React from 'react'
import NavBar from './NavBar'
export default class ContextTest extends
React.Component{
constructor(props){
super(props)
this.state={user:{name:'小童',account:'baizhan'}}
}
render(){
return <div>
<NavBar user={this.state.user}/>
</div>
}
}
//NavBar.js
import React, { Component } from 'react'
import UserInfo from './UserInfo'
export default class NabBar extends
Component {
render() {
return (
<div style= {{height:'50px',background:'#ccc',display:'flex',justifyContent:'space-around',alignItems:'center'}}>
NabBar的内容
<UserInfo user={this.props.user}/>
</div>
)
}
}
//UserInfo.js
import React, { Component } from 'react'
export default class UserInfo extends
Component {
render() {
return (
<div>你好,欢迎 {this.props.user.account}</div>
)
}
}
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
何时使用 Context
如果我们要在组件树中去共享某些数据,并且要避免通过中间元素传递 props,则可以使用Context来实现。
Context_应用
1、创建Context对象
React.createContext(defaultValue)
const MyContext = React.createContext(defaultValue);
2、 Context 的 Provider 组件
Contenxt的Provider组件用来提供其它组件要共享的数据。
设置 value 属性来设置要共享的数据。
<MyContext.Provider value={/* 某个值 */}>
3、Context 的 Provider 组件
Contenxt的Provider组件用来提供其它组件要共享的数据。
设置 value 属性来设置要共享的数据。
<MyContext.Provider value={/* 某个值 */}>
提示:我们把要使用共享数据的组件称为 消费组件 。
Class.contextType
为组件类添加 contextType 属性,从而获取Context对象。
MyClass.contextType = MyContext;
提示:
挂载在 class 上的 contextType 属性会被重赋值为一个由
React.createContext() 创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。
你可以在任何生命周期中访问到它,包括 render 函数中。
//MyContext.js
import React from "react";
export default
React.createContext({name:'',account:'xxx'})
//ContextTest.js
import React from 'react'
import MyContext from './MyContext'
import NavBar from './NavBar'
export default class ContextTest extends
React.Component {
constructor(props) {
super(props)
this.state = { user: { name: '小童',account: 'xiaotong' } }
}
render() {
return <div>
{/* 使用Provider组件提供数据 */}
<MyContext.Provider value= {this.state.user }>
<NavBar />
</MyContext.Provider>
</div>
}
}
//NabBar.js
import React, { Component } from 'react'
import UserInfo from './UserInfo'
export default class NabBar extends
Component {
render() {
return (
<div style= {{height:'50px',background:'#ccc',display:'flex',justifyContent:'space-around',alignItems:'center'}}>
NabBar的内容
<UserInfo />
</div>
)
}
}
//UserInfo.js
import React, { Component } from 'react'
import MyContext from './MyContext'
export default class UserInfo extends
Component {
render() {
console.log(this.context)
return (
<div>你好,欢迎 {this.context.account}</div>
)
}
}
//设置类的contentType属性,从而获取context对象
UserInfo.contextType=MyContext
函数组件订阅Context
Context.Consumer
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
//ContextTest.js
render() {
return <div>
{/* 使用Provider组件提供数据 */}
<MyContext.Provider value= {this.state.user }>
<NavBar />
<MyContext.Consumer>
{value=><div>
<h3>函数组件</h3>
获取到的账户:{value.account}
</div>}
</MyContext.Consumer>
</MyContext.Provider>
</div>
}
Context更新
当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。
//ContextTest.js
render() {
return <div>
<button onClick= {()=>this.setState({user:{name:"xiaotong",account:'xiaotong123'}})}>更新user</button>
{/* 使用Provider组件提供数据 */}
<MyContext.Provider value={this.state.user }>
<NavBar />
<MyContext.Consumer>
{value=><div>
<h3>函数组件</h3>
获取到的账户:{value.account}
</div>}
</MyContext.Consumer>
</MyContext.Provider>
</div>
}
提示:消费组件是否重新渲染不受shouldComponentUpdate的控制。
其父组件使用shouldComponentUpdate停止了渲染或者消费组件本身使用shouldComponentUpdate停止渲染,也不影响消费组件的继续更新。
//NavBar.js
export default class NabBar extends
Component {
shouldComponentUpdate(){
//当前这个非消费组件会停止更新渲染,但是它的子组件UserInfo是Context的消费组件,还会继续更新
return false
}
render() {
console.log('navbar')
return (
<div style= {{height:'50px',background:'#ccc',display:'flex',justifyContent:'space-around',alignItems:'center'}}>
NabBar的内容
<UserInfo />
</div>
)
}
}
//UserInfo.js
import React, { Component } from 'react'
import MyContext from './MyContext'
export default class UserInfo extends
Component {
shouldComponentUpdate() {
//虽然返回了false,但是context的值发生变化了,我还是要重新渲染的
return false
}
render() {
return (
<div>你好,欢迎{this.context.account}</div>
)
}
}
UserInfo.contextType = MyContext
Fragments
场景一:
render() {
return (
<p></p>
<p></p>
)
}
提示: react当中,组件的界面只能有一个根标签。
场景二:
render() {
return (
// 最外层的这个div可能根本不需要,不想让它渲染到页面上
<div>
<p></p><p></p>
</div>
)
}
Fragments 允许你将子元素包裹在一起,而无需向 DOM 添加额外节点。
render() {
return (
<React.Fragment>
<p></p>
<p></p>
</React.Fragment>
)
}
最终页面渲染结果:
1、Fragments 短语法
使用 <></> 代替 <React.Fragment></React.Fragment>
render() {
return (
<>
<p></p>
<p></p>
</>
)
}
提示:
因为Fragments最终不会被渲染为DOM,所以不要在Framents上面绑定事件或者设置一些其它属性,目前只支持设置key属性。
错误边界_概念
通常某一个组件中发生的错误会导致整个应用崩溃,页面一片空白。
//App.js
render() {
return (
<>
<ContextTest />
<SomethingWrong />
</>
)
}
//SomethingWrong.js
export default class SomethingWrong extends
Component {
constructor(){
// 未调用super,则会抛出错误
}
render() {
return (
<div>
<Child/>
</div>
)
}
}
如果我们想让未出现错误的组件还能继续渲染,则可以使用 错误边界 。
错误边界 是一种 React 组件。
错误边界 可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它可以渲染出备用 UI。
错误边界 在渲染期间、生命周期方法和整个子组件树的构造函数中捕获错误。
提示: 错误边界无法捕获以下场景中产生的错误:
1、事件处理
2、异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)3、它自身抛出来的错误(并非它的子组件)
错误边界_应用
1、 定义一个错误边界的组件
class 组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么这个组件就变成一个错误边界的组件。
import React, { Component } from 'react'
export default class ErrorBoundary extends
Component {
constructor() {
super()
this.state = { }
}
componentDidCatch(error, errorInfo) {
// 捕获到子组件树当中发生的错误时调用
this.setState({
error: error,
errorInfo: errorInfo
})
}
render() {
return (
this.state.errorInfo ? <div>奥,糟糕!!发生了一些错误:
错误信息如下:<br />
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</div> : this.props.children
)
}
}
错误边界的使用
//App.js
render() {
return (
<>
<ContextTest />
{/* 使用错误边界组件包裹的组件树,发生的错误都会被这个错误边界捕获 */}
<ErrorBoundary>
<SomethingWrong />
</ErrorBoundary>
</>
)
}
错误边界无法捕获的错误
错误边界无法捕获的错误,比如事件处理,异步操作。可以使用原生js支持的一些方法去捕获。
try/catch
onClick=()=>{
try{
//使用一个未定义的变量a,会报错
console.log(a)
}catch(e){
console.log(e)
this.setState({hasError:true})
}
}
render() {
return (
<div>
<button onClick={this.onClick}>点击</button>
{this.state.hasError?<div>出现错误了</div>:null}
</div>
)
}
window.onerror
window.onerror可以捕捉语法错误,也可以捕捉运行时错误,只要在当前window执行的Js脚本出错都会捕捉到。
window.onerror=function(err){
console.log(err)
}
Refs & DOM
Refs 提供了一种方式,允许我们访问DOM 元素。
<div ref={ref}></div>
创建 Refs
使用 React.createRef() 创建的。
通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。
constructor(props) {
super(props);
this.myRef = React.createRef();
}
使用Refs
给对应的React 元素设置 ref 属性,则相当于使用 ref 去存储DOM 节点的引用。
render() {
return <input ref={this.myRef} />;
}
访问Refs
当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。
componentDidMount(){
const node = this.myRef.current;
node.focus()
}
提示:
React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMount 或 componentDidUpdate
生命周期钩子触发前更新。