第 8 章 React 拡張機能
5. コンテキスト
1. コード
/* index.jsx */
import React, {
Component } from 'react'
import './index.css'
//创建Context对象
const MyContext = React.createContext()
const {
Provider,Consumer} = MyContext
export default class A extends Component {
state = {
username:'tom',age:18}
render() {
const {
username,age} = this.state
return (
<div className="parent">
<h3>我是A组件</h3>
<h4>我的用户名是:{
username}</h4>
<Provider value={
{
username,age}}>
<B/>
</Provider>
</div>
)
}
}
class B extends Component {
render() {
return (
<div className="child">
<h3>我是B组件</h3>
<C/>
</div>
)
}
}
/* class C extends Component {
//声明接收context
static contextType = MyContext
render() {
const {username,age} = this.context
return (
<div className="grand">
<h3>我是C组件</h3>
<h4>我从A组件接收到的用户名:{username},年龄是{age}</h4>
</div>
)
}
} */
function C(){
return (
<div className="grand">
<h3>我是C组件</h3>
<h4>我从A组件接收到的用户名:
<Consumer>
{
value => `${
value.username},年龄是${
value.age}`}
</Consumer>
</h4>
</div>
)
}
2. まとめ
- 理解する
コンポーネント間の通信方法。[祖先コンポーネント]と[子孫コンポーネント]間の通信によく使用されます。
- 使用
1) 创建Context容器对象:
const XxxContext = React.createContext()
2) 渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={
数据}>
子组件
</xxxContext.Provider>
3) 后代组件读取数据:
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>
- 知らせ
アプリケーション開発では、通常、コンテキストは使用されず、そのカプセル化された反応プラグインが使用されるのが一般的です。
6. コンポーネントの最適化
1. コンポーネントに関する 2 つの質問
- setState() が実行されている限り、状態データが変更されていなくてもコンポーネントは re-render() を実行します ==> 効率が低い
- 現在のコンポーネントのみが re-render() の場合、サブコンポーネントが親コンポーネントのデータを使用していない場合でも、サブコンポーネントは自動的に再レンダリングされます ==> 効率が低い
2. 高効率なアプローチ
- コンポーネントの状態または props データが変更された場合にのみ Re-render()
3. 理由
- コンポーネント内の shouldComponentUpdate() は常に true を返します
4.解決する
办法1:
重写shouldComponentUpdate()方法
比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:
使用PureComponent
PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
注意:
只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
不要直接修改state数据, 而是要产生新数据
项目中一般使用PureComponent来优化
5. コード
/* src/App.jsx */
import React, {
PureComponent } from 'react'
import './index.css'
export default class Parent extends PureComponent {
state = {
carName:"奔驰c36",stus:['小张','小李','小王']}
addStu = ()=>{
/* const {stus} = this.state
stus.unshift('小刘')
this.setState({stus}) */
const {
stus} = this.state
this.setState({
stus:['小刘',...stus]})
}
changeCar = ()=>{
//this.setState({carName:'迈巴赫'})
const obj = this.state
obj.carName = '迈巴赫'
console.log(obj === this.state);
this.setState(obj)
}
/* shouldComponentUpdate(nextProps,nextState){
// console.log(this.props,this.state); //目前的props和state
// console.log(nextProps,nextState); //接下要变化的目标props,目标state
return !this.state.carName === nextState.carName
} */
render() {
console.log('Parent---render');
const {
carName} = this.state
return (
<div className="parent">
<h3>我是Parent组件</h3>
{
this.state.stus}
<span>我的车名字是:{
carName}</span><br/>
<button onClick={
this.changeCar}>点我换车</button>
<button onClick={
this.addStu}>添加一个小刘</button>
<Child carName="奥拓"/>
</div>
)
}
}
class Child extends PureComponent {
/* shouldComponentUpdate(nextProps,nextState){
console.log(this.props,this.state); //目前的props和state
console.log(nextProps,nextState); //接下要变化的目标props,目标state
return !this.props.carName === nextProps.carName
} */
render() {
console.log('Child---render');
return (
<div className="child">
<h3>我是Child组件</h3>
<span>我接到的车是:{
this.props.carName}</span>
</div>
)
}
}
/* src/index.css */
.parent{
background-color: orange;
padding: 10px;
}
.child{
background-color: gray;
margin-top: 30px;
padding: 10px;
}
7.renderProps
1. コンテンツを含む構造 (タグ) をコンポーネントに動的に渡すにはどうすればよいですか?
- Vue 中:
- スロットテクノロジーを使用します。つまり、コンポーネントタグ本体を介して構造を渡します。
- <A><B/></A>
- React 中:
- 子プロパティを使用する: コンポーネントのタグ本体を介して構造体を渡します。
- レンダー小道具を使用する: コンポーネントのラベル属性を介して構造体を渡し、データを運ぶことができます。一般に、レンダー関数属性が使用されます。
2.子供用小道具
<A>
<B>xxxx</B>
</A>
{
this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
3. 小道具をレンダリングする
<A render={
(data) => <C data={
data}></C>}></A>
A组件: {
this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {
this.props.data}
4. コード
/* src/App.jsx */
import React, {
Component } from 'react'
import './index.css'
export default class Parent extends Component {
render() {
return (
<div className="parent">
<h3>我是Parent组件</h3>
<A render={
(name)=><B name={
name}/>}/>
</div>
)
}
}
class A extends Component {
state = {
name:'tom'}
render() {
console.log(this.props);
const {
name} = this.state
return (
<div className="a">
<h3>我是A组件</h3>
{
this.props.render(name)}
</div>
)
}
}
class B extends Component {
render() {
console.log('B--render');
return (
<div className="b">
<h3>我是B组件,{
this.props.name}</h3>
</div>
)
}
}
/* src/index.css */
.parent{
background-color: orange;
padding: 10px;
}
.a{
background-color: gray;
margin-top: 30px;
padding: 10px;
}
.b{
background-color: skyblue;
margin-top: 30px;
padding: 10px;
}
8. エラー境界
1. 理解する
- エラー境界: 子孫コンポーネントのエラーをキャプチャし、代替ページをレンダリングするために使用されます。
2. 特長
- 子孫コンポーネントのライフサイクルによって生成されたエラーのみをキャプチャできます。独自のコンポーネントによって生成されたエラーや、合成イベントおよびタイマー内の他のコンポーネントによって生成されたエラーはキャプチャできません。
3. 使用方法
- getDerivedStateFromError 連携コンポーネントDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
4. コード
4.1 子供
/* src/Child.jsx */
import React, {
Component } from 'react'
export default class Child extends Component {
state = {
users:[
{
id:'001',name:'tom',age:18},
{
id:'002',name:'jack',age:19},
{
id:'003',name:'peiqi',age:20},
]
// users:'abc'
}
render() {
return (
<div>
<h2>我是Child组件</h2>
{
this.state.users.map((userObj)=>{
return <h4 key={
userObj.id}>{
userObj.name}----{
userObj.age}</h4>
})
}
</div>
)
}
}
4.2 親
/* src/Parent.jsx */
import React, {
Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
state = {
hasError:'' //用于标识子组件是否产生错误
}
//当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
static getDerivedStateFromError(error){
console.log('@@@',error);
return {
hasError:error}
}
componentDidCatch(){
console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
}
render() {
return (
<div>
<h2>我是Parent组件</h2>
{
this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child/>}
</div>
)
}
}
4.3 アプリ
/* src/App.jsx */
import React, {
Component,Fragment } from 'react'
import Demo from './Parent'
export default class App extends Component {
render() {
return (
<Fragment>
<Demo/>
</Fragment>
)
}
}
9. コンポーネントの通信方式のまとめ
1. コンポーネント間の関係
- 親子コンポーネント
- 兄弟コンポーネント (ネストされていないコンポーネント)
- 祖父母コンポーネントと孫コンポーネント (クロスレベルコンポーネント)
2. いくつかの通信方法
1.props:
(1).children props
(2).render props
2.消息订阅-发布:
pubs-sub、event等等
3.集中式管理:
redux、dva等等
4.conText:
生产者-消费者模式
3. より良いマッチング方法
- 親子コンポーネント: 小道具
- Brother コンポーネント: メッセージのサブスクリプション発行、集中管理
- 祖父母コンポーネントと孫コンポーネント (クロスレベル コンポーネント): メッセージ サブスクリプションの発行、集中管理、コンテキスト (開発にはあまり使用されず、プラグインのパッケージ化に多く使用されます)