react-学习之路3
Posted Keep runing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react-学习之路3相关的知识,希望对你有一定的参考价值。
生命周期我们也介绍完了;上一节有几个遗留问题,我们这节来补一下;
1、组件传值context
先来看一下怎么使用吧
// 首先创建一个Context
// 创建一个文件 AppContext.js
import React,{Compontent} from "react"
export const Context = React.createContext()
export const Provider = Context.Provider
export const Consumer = Context.Consumer
// index.js 中引入
import { Provider } from "./AppContext";
const store = {
user: "snail",
name:"xxx"
};
ReactDOM.render(
<Provider value={store}>
<App />
</Provider>,
document.getElementById("root")
);
// 创建层级文件 App->Demo->Demo2->Demo3
// Demo2
import React, { Component } from "react";
import { Consumer } from "../AppContext";
import Demo3 from "./Demo3";
export default class Demo2 extends Component {
render() {
return (
<div>
<Consumer>
{/* ctx就是传过来的state */}
{(ctx) => ctx.user} // 这里实际输出 snail
</Consumer>
<Demo3 />
</div>
);
}
}
// Demo 3
import React, { Component } from 'react'
import {Consumer} from "../AppContext"
export default class Demo3 extends Component {
render() {
return (
<div>
<Consumer>
{ctx=>ctx.name} // 这里实际输出xxx
</Consumer>
</div>
)
}
}
// vue 的provide inject 来源就是context
// provide 提供值 Consumer 消费值
函数组件中 使用uesContext 来引入上下文
// Demo3
import React,{useContext}from 'react'
import {Context} from '../AppContext'
export default function Demo3() {
const ctx = useContext(Context)
const {user,name} = ctx
return (
<div>
{user} ---- {name}
</div>
)
}
2、高阶组件 HOC
高阶组件本身就一个函数,它接受一个组件返回另一个组件
首先看一下不使用写法: (官方的案例改写) https://react.docschina.org/docs/higher-order-components.html
// 定义一个全局数据源
// 数据源
let DataSource = {
list:[
"list-1","list-2","list-3"
],
getComments:function(){
return this.list
},
getBlogPost:function(id){
return this.list[id]
},
addChangeListener:()=>{console.log("addChangeListener")},
removeChangeListener:()=>{console.log("removeChangeListener")},
}
export default DataSource
// 列表页
// 组件从外部订阅数据并进行渲染评论列表 list
import React, { useState, useEffect } from "react";
import DateSource from "./DataSource"
export default function CommonList(props) {
const [data, setData] = useState(DateSource.getComments());
useEffect(() => {
DateSource.addChangeListener();
return () => {
DateSource.removeChangeListener();
};
}, [data]);
return (
<ul>
{props.data.map((item,index) => {
return <li key={index}>{item}</li>;
})}
</ul>
);
}
// 订阅单个博客文章组件 id
import React, { useState, useEffect } from "react";
import DateSource from "./DataSource";
export default function BlogPost(props) {
console.log(props)
const [data, setData] = useState(DateSource.getBlogPost(props.id));
useEffect(() => {
DateSource.addChangeListener();
return () => {
DateSource.removeChangeListener();
};
}, [data]);
return (
<ul>
{<li>{data}</li>}
</ul>
);
}
上面两个组件分别进行了数据监听和数据移除 除了数据源不一样,监听函数都是可以公用的;这是我们就可以提出组件进行封装;
下面请看栗子:
// 高阶组件 实现
import React, { useState, useEffect } from "react";
import DateSource from "./DataSource";
import PropTypes from 'prop-types'
export default (WrappedComponent,initData) => (props)=>{
const [data, setData] = useState(initData);
useEffect(() => {
DateSource.addChangeListener();
return () => {
DateSource.removeChangeListener();
};
}, [data]);
return <WrappedComponent data={Array.isArray(data)?data:[data]} {...props}/>
}
公共组件:
// WithSubscription.js
import React from "react";
export default function CommonList(props) {
return (
<ul>
{props.data.map((item,index) => {
return <li key={index}>{item}</li>;
})}
</ul>
);
}
使用:
export default class Demo extends Component {
constructor(props) {
super(props);
this.state = {};
}
DemoCmp1=WithSubscription(CommonList,DataSource.getComments());
DemoCmp2=WithSubscription(CommonList,DataSource.getBlogPost(1));
render() {
return (
<>
<this.DemoCmp1 age="1111" />
<this.DemoCmp2 age="2222" />
</>
);
}
}}
好了,基本的使用大概就是这样;也可以用class 去实现 是一样的;
使用HOC 需要建议:
1、不要改变原始组件,使用组合组件
HOC不应该修改传入的组件,而应该使用组合的方式,通过组件包装在容器组件中实现功能
2、将不相关的props传递给被包裹的组件
HOC为组件添加特性,自身不因该大幅改变约定,HOC返回的组件与原组件保持类似的接口;HOC应穿透与自身无关的props
3、最大化可组合性
4、包装显示名称以便轻松调试,HOC 创建的容器组件会与任何其他组件一样,会显示在 React Developer Tools 中。为了方便调试,请选择一个显示名称,以表明它是 HOC 的产物。
使用HOC 需要注意事项:
1、不要再render方法中使用HOC
官方解答:
React 的 diff 算法(称为协调)使用组件标识来确定它是应该更新现有子树还是将其丢弃并挂载新子树。如果从 render
返回的组件与前一个渲染中的组件相同(===
),则 React 通过将子树与新子树进行区分来递归更新子树。如果它们不相等,则完全卸载前一个子树。
通常,你不需要考虑这点。但对 HOC 来说这一点很重要,因为这代表着你不应在组件的 render 方法中对一个组件应用 HOC:
render() {
// 每次调用 render 函数都会创建一个新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
return <EnhancedComponent />;
}
这不仅仅是性能问题 - 重新挂载组件会导致该组件及其所有子组件的状态丢失。
如果在组件之外创建 HOC,这样一来组件只会创建一次。因此,每次 render 时都会是同一个组件。一般来说,这跟你的预期表现是一致的。
在极少数情况下,你需要动态调用 HOC。你可以在组件的生命周期方法或其构造函数中进行调用。
2、务必复制静态方法
有时在 React 组件上定义静态方法很有用。例如,Relay 容器暴露了一个静态方法 getFragment 以方便组合 GraphQL 片段。
但是,当你将 HOC 应用于组件时,原始组件将使用容器组件进行包装。这意味着新组件没有原始组件的任何静态方法。
// 定义静态函数
WrappedComponent.staticMethod = function() {/*...*/}
// 现在使用 HOC
const EnhancedComponent = enhance(WrappedComponent);
// 增强组件没有 staticMethod
typeof EnhancedComponent.staticMethod === 'undefined' // true
为了解决这个问题,你可以在返回之前把这些方法拷贝到容器组件上:
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
// 必须准确知道应该拷贝哪些方法 :(
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
但要这样做,你需要知道哪些方法应该被拷贝。你可以使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法:
import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
hoistNonReactStatic(Enhance, WrappedComponent);
return Enhance;
}
除了导出组件,另一个可行的方案是再额外导出这个静态方法。
// 使用这种方式代替...
MyComponent.someFunction = someFunction;
export default MyComponent;
// ...单独导出该方法...
export { someFunction };
// ...并在要使用的组件中,import 它们
import MyComponent, { someFunction } from './MyComponent.js'
3、Refs不会被传递
虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件
以上是关于react-学习之路3的主要内容,如果未能解决你的问题,请参考以下文章