什么是高阶组件
高阶组件就是一个 React 组件包裹着另外一个 React 组件
高阶组件的思路则是函数式的,每一个扩展(Enhance)就是一个函数,接受要扩展的组件作为参数。如果要进行 3 个扩展,那么则可以级联,看起来就是:
const newComponent = Enhance3(Enhance2(Enhance1(MyComponent)));
高阶组件的方式则使得每一个 Enhance 以及被扩展的组件都只关心自己手里那点事。Enhance 不知道别人会怎么用它,被扩展的组件也不关心别人会怎么扩展它。负责人是那个将它们连在一起的”水管工”,即最后写串联代码的人。
高阶组件的用法虽然用到了 ES6 的类继承,但是实际上却只是把它当个工具使用,而不是真的借助于 OO 的继承。在 React 中使用高阶组件部分替代 Mixins,仍然是非常函数化的思维方式,即针对 ”转换” 编程。只不过是组件定义替代了函数而已。
高阶组件可以做什么
代码复用,逻辑抽象,抽离底层准备(bootstrap)代码
渲染劫持
State 抽象和更改
Props 更改
import { Component } from "React" ;
export const Enhance = (ComposedComponent) => class extends Component {
constructor() {
this.state = { data: null };
}
componentDidMount() {
this.setState({ data: 'Hello' });
}
render() {
return <ComposedComponent {...this.props} data={this.state.data} />;
}
};
import { Component } from "React";
import { Enhance } from "./Enhance";
class MyComponent = class extends Component {
render() {
if (!this.props.data) return <div>Waiting...</div>;
return <div>{this.data}</div>;
}
}
export default Enhance(MyComponent); // Enhanced component`
实现方法
Props Proxy: HOC 对传给 WrappedComponent W 的 porps 进行操作
Props Proxy 可以做什么:
更改 props
通过 refs 获取组件实例
抽象 state
把 WrappedComponent 与其它 elements 包装在一起
更改 props
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
const newProps = {
user: currentLoggedInUser
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
}
通过 refs 获取组件实例
function refsHOC(WrappedComponent) {
return class RefsHOC extends React.Component {
proc(wrappedComponentInstance) {
wrappedComponentInstance.method()
}
render() {
const props = Object.assign({}, this.props, {ref: this.proc.bind(this)})
return <WrappedComponent {...props}/>
}
}
}
抽象 state
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
constructor(props) {
super(props)
this.state = {
name: ''
}
this.onNameChange = this.onNameChange.bind(this)
}
onNameChange(event) {
this.setState({
name: event.target.value
})
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onNameChange
}
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
}
把 WrappedComponent 与其它 elements 包装在一起
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
return (
<div style={{display: 'block'}}>
<WrappedComponent {...this.props}/>
</div>
)
}
}
}
Inheritance Inversion: HOC 继承 WrappedComponent W
可以用反向继承高阶组件做什么?
渲染劫持(Render Highjacking)
操作 state
渲染劫持
它被叫做渲染劫持是因为高阶组件控制了 WrappedComponent 生成的渲染结果,并且可以做各种操作。
通过渲染劫持你可以:
读取、添加、修改、删除』任何一个将被渲染的 React Element 的 props
在渲染方法中读取或更改 React Elements tree,也就是 WrappedComponent 的 children
根据条件不同,选择性的渲染子树
给子树里的元素变更样式
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
return super.render()
}
}
}
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
if (this.props.loggedIn) {
return super.render()
} else {
return null
}
}
}
}
操作 state
高阶组件可以 『读取、修改、删除』WrappedComponent 实例的 state,如果需要也可以添加新的 state。需要记住的是,你在弄乱 WrappedComponent 的 state,可能会导致破坏一些东西。通常不建议使用高阶组件来读取或添加 state,添加 state 需要使用命名空间来防止与 WrappedComponent 的 state 冲突。
export function IIHOCDEBUGGER(WrappedComponent) {
return class II extends WrappedComponent {
render() {
return (
<div>
<h2>HOC Debugger Component</h2>
<p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
<p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
{super.render()}
</div>
)
}
}
}
实例
function hoc(ComponentClass) {
return class HOC extends ComponentClass {
render() {
if (this.state.success) {
return super.render()
}
return <div>Loading...</div>
}
}
}
@hoc
export default class ComponentClass extends React.Component {
state = {
success: false,
data: null
};
async componentDidMount() {
const result = await fetch(...请求); this.setState({
success: true,
data: result.data
});
}
render() {
return <div>主要内容</div>
}
}
从上面的例子可以得出
Higher-Order Components = Decorators + Components