# 高阶组件
高阶组件是重用组件逻辑的一项高级技术。
高阶组件(HOC全称Higher-order component)是一种React的进阶使用方法,主要还是为了便于组件的复用。
高阶组件类似于高阶函数,接收 React 组件作为输入,输出一个新的 React 组件。高阶组件让代码更具有复用性、逻辑性与抽象特征。可以对 render 方法作劫持,也可以控制 props 与 state。
通俗来讲高阶组件是一个函数,能够接受一个组件并返回一个高级的组件。
var newComponent = higherOrderComponent(oldComponent);
最简单的HOC实现是这个样子的:
//定义高阶组件
function HOCFactory (WrappedComponent){
//返回一个中间组件,在其中可包含复用逻辑
return class extends Component{
return <WrappedComponent {...this.props}/>
}
}
class MyComponent extends Component{}
//创建高阶组件
export default HOCFactory(MyComponent)
# 什么时候使用高阶组件?
在React开发过程中,发现有很多情况下,组件需要被"增强",比如说给组件添加或者修改一些特定的props,一些权限的管理,或者一些其他的优化之类的。而如果这个功能是针对多个组件的,同时每一个组件都写一套相同的代码,明显显得不是很明智,所以就可以考虑使用高阶组件。
HOC:
例如:react-redux的connect方法就是一个HOC,他获取wrappedComponent,在connect中给wrappedComponent添加需要的props。
# 高阶组件实现
实现高阶组件的方法有如下两种。
- 属性代理(props proxy)。属性组件通过被包裹的 React 组件来操作 props。
- 反向代理(inheritance inversion)。高阶组件继承于被包裹的 React 组件。
# 属性代理
属性代理是常见高阶组件的实现方法。
const MyContainer = (WrappedComponent) => {
return class extends Component {
//可复用逻辑
render() {
return (
<WrappedComponent
{...props}
/>
)
}
}
}
export default MyContainer;
原始组件想要具备高阶组件对它的修饰,有这样两种方式:
# 方式一:传统方式
export default function HOCFactory (WrappedComponent){
return class extends Component{
return <WrappedComponent {...this.props}/>
}
}
class MyComponent extends Component{}
export default HOCFactory(MyComponent)
# 方式二:ES7 decorator(装饰器)
ES7 添加了 decorator 的属性,我们可以通过 decorator 来转换,以此简化高阶组件的调用。
export default function HOCFactory (WrappedComponent){
return class extends Component{
return <WrappedComponent {...this.props}/>
}
}
@HOCFactory
class MyComponent extends Component{}
export default MyComponent;
生命周期
HOC didmount -> (WrappedComponent didmount) -> (WrappedComponent will unmount) -> Hoc will unmount
# 作用
高阶组件可以控制 props、通过 refs 使用引用、抽象 state 和使用其他元素包裹 WrappedComponent。
# 控制 props
我们可以读取、增加、编辑或是移除从 WrappedComponent 传进来的 props,但需要小心删除与编辑重要的 props。应该尽量对高阶组件的 props 作新的命名以防止混淆。
const myContainer = (WrappedComponent)=>{
return class extends React.Component{
render(){
const newProps = {
...this.props,
text: nextText
}
return (<WrappedComponent
{...this.props}
/>)
}
}
}
export default myContainer;
当调用该高阶组件时,就可以使用 text 这个新的 props了。
# 通过 refs 使用引用
在高阶组件中,可以接受 refs 使用 WrappedComponent 的引用。
//子组件
class Child extends React.Component{
render(){
return <input />
}
}
//高阶组件
function logProps(WrappedComponent) {
class LogProps extends React.Component {
render() {
const {forwardedRef, ...rest} = this.props;
return <WrappedComponent ref={forwardedRef} {...rest} />;
}
}
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
//通过logProps 生成一个新组件
const logProps=logProps(Child);
class Father extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current); // 输出为 child
}
render(){
return <LogProps ref={this.myRef}/>
}
}
# 抽象 state
可以通过 WrappedComponent 提供的 props 和回调函数抽象 state。将原组件抽象为展示型组件,分离内部状态 。
const MyContainer = (WrappedComponent) => {
return class extends Component {
constructor(props);
super(props);
this.state = {
name: ''
};
}
onNameChange(text) {
this.setState({
name: text
});
}
render() {
const newProps = {
name = {
value: this.state.name,
onChangeText: this.onNameChange
}
}
return (
<WrappedComponent
{...this.props}
{...newProps}
/>
);
}
}
export default MyContainer;
使用
@MyContainer
class MyComponent extends Component {
render() {
return (
<TextInput
{...this.props.name}
/>
);
}
}
# 使用其他元素包裹 WrappedComponent
可以使用其他元素包裹 WrappedComponent。
const MyContainer = (WrappedComponent) => {
return class extends Component {
render() {
return (
<View>
<WrappedComponent />
</View>
);
}
}
}
export default MyContainer;
# HOC可以做什么?
- 代码复用,代码模块化
- 增删改props
- 渲染劫持
其实,除了代码复用和模块化,HOC做的其实就是劫持,由于传入的wrappedComponent是作为一个child进行渲染的,上级传入的props都是直接传给HOC的,所以HOC组件拥有很大的权限去修改props和控制渲染。
# 代码复用
例如:组件A的代码 与组件B的代码 部分逻辑相同,针对这种情况,我们可以采用高阶组件去复用这部分逻辑。
https://segmentfault.com/a/1190000008112017
https://juejin.im/post/5bd68bc5518825287847a860