react之高阶组件

1 高阶组件

  • 高阶组件是参数为组件,返回值为新组件的函数
  • 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件
  • HOC是 React 中用于复用组件逻辑的一种高级技巧; 是一种组件组合的设计模式
const EnhancedComponent = higherOrderComponent(WrappedComponent);

2 高阶组件示例

// CommentList组件
class CommentList extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      // 假设 "DataSource" 是个全局范围内的数据源变量
      comments: DataSource.getComments()
    };
  }
  componentDidMount() {
    // 订阅更改
    DataSource.addChangeListener(this.handleChange);
  }
  componentWillUnmount() {
    // 清除订阅
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    // 当数据源更新时,更新组件状态
    this.setState({
      comments: DataSource.getComments()
    });
  }

  render() {
    return (
      <div>
        {this.state.comments.map((comment) => (
          <Comment comment={comment} key={comment.id} />
        ))}
      </div>
    );
  }
}

// BlogPost组件
class BlogPost extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      blogPost: DataSource.getBlogPost(props.id)
    };
  }

  componentDidMount() {
    DataSource.addChangeListener(this.handleChange);
  }

  componentWillUnmount() {
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    this.setState({
      blogPost: DataSource.getBlogPost(this.props.id)
    });
  }

  render() {
    return <TextBlock text={this.state.blogPost} />;
  }
}
// CommentList 和 BlogPost组件 不同 - 它们在 DataSource 上调用不同的方法,且渲染不同的结果。但它们的大部分实现都是一样的:

// 1. 在挂载时,向 DataSource 添加一个更改侦听器。
// 2. 在侦听器内部,当数据源发生变化时,调用 setState。
// 3. 在卸载时,删除侦听器。
// 对于订阅了 DataSource 的组件,比如 CommentList 和 BlogPost,我们可以编写一个创建组件函数。该函数将接受一个子组件作为它的其中一个参数,该子组件将订阅数据作为 prop。让我们调用函数 withSubscription

const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);

const BlogPostWithSubscription = withSubscription(
  BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id)
);

// 第一个参数是被包装组件。第二个参数通过 DataSource 和当前的 props 返回我们需要的数据。




// 高阶组件withSubscription实现
function withSubscription(WrappedComponent, selectData) {
  // ...并返回另一个组件...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ...负责订阅相关的操作...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... 并使用新数据渲染被包装的组件!
      // 请注意,我们可能还会传递其他属性
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

3 高阶组件注意点

  • 不要改变原始组件;尽量使用组件组合的形式
function logProps(InputComponent) {
  InputComponent.prototype.componentDidUpdate = function(prevProps) {
    console.log('Current props: ', this.props);
    console.log('Previous props: ', prevProps);
  };
  // 返回原始的 input 组件,暗示它已经被修改。
  return InputComponent;
}

// 每次调用 logProps 时,增强组件都会有 log 输出。
const EnhancedComponent = logProps(InputComponent);


// 解析:不要试图在 HOC 中修改组件原型(或以其他方式改变它); 比如修改了原始组件的原型,其一:那么输入组件再也无法像 HOC 增强之前那样使用了;其二:如果你再用另一个同样会修改 componentDidUpdate 的 HOC 增强它,那么前面的 HOC 就会失效!


// 组件组合:HOC 不应该修改传入组件,而应该使用组合的方式,通过将组件包装在容器组件中实现功能
function logProps(WrappedComponent) {
  return class extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('Current props: ', this.props);
      console.log('Previous props: ', prevProps);
    }
    render() {
      // 将 input 组件包装在容器中,而不对其进行修改。Good!
      return <WrappedComponent {...this.props} />;
    }
  }
}
  • 高阶组件透传除了ref之外的所有props属性
虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。