使用高阶组件来改变样式

有时有一些组件可能只需要很少的一部分state来维护一些很简单的交互, 我们也有充足的理由把这些组件作为可复用的组件.

例子. Carousel组件的交互

这个高阶组件会持有当前幻灯片的index并且提供前进和回退的功能.

  1. // 高阶组件
  2. import React from 'react'
  3. // 这个高阶组件其实可以被命名的更加通俗易懂, 比如Counter或者Cycle
  4. const CarouselContainer = (Comp) => {
  5. class Carousel extends React.Component {
  6. constructor() {
  7. super();
  8. this.state = {
  9. index: 0
  10. };
  11. this.previous = () => {
  12. const { index } = this.state;
  13. if (index > 0) {
  14. this.setState({index: index - 1})
  15. }
  16. };
  17. this.next = () => {
  18. const { index } = this.state;
  19. this.setState({index: index + 1})
  20. }
  21. }
  22. render() {
  23. return (
  24. <Comp
  25. {...this.props}
  26. {...this.state}
  27. previous={this.previous}
  28. next={this.next}/>
  29. )
  30. }
  31. }
  32. return Carousel
  33. };
  34. export default CarouselContainer;

使用高阶组件

  1. // 纯UI component
  2. const Carousel = ({ index, ...props }) => {
  3. const length = props.length || props.children.length || 0;
  4. const sx = {
  5. root: {
  6. overflow: 'hidden'
  7. },
  8. inner: {
  9. whiteSpace: 'nowrap',
  10. height: '100%',
  11. transition: 'transform .2s ease-out',
  12. transform: `translateX(${index % length * -100}%)`
  13. },
  14. child: {
  15. display: 'inline-block',
  16. verticalAlign: 'middle',
  17. whiteSpace: 'normal',
  18. outline: '1px solid red',
  19. width: '100%',
  20. height: '100%'
  21. }
  22. };
  23. const children = React.Children.map(props.children, (child, i) => {
  24. return (
  25. <div style={sx.child}>
  26. {child}
  27. </div>
  28. )
  29. });
  30. return (
  31. <div style={sx.root}>
  32. <div style={sx.inner}>
  33. {children}
  34. </div>
  35. </div>
  36. )
  37. };
  38. // 最后的Carousel组件
  39. const HeroCarousel = (props) => {
  40. return (
  41. <div>
  42. <Carousel index={props.index}>
  43. <div>Slide one</div>
  44. <div>Slide two</div>
  45. <div>Slide three</div>
  46. </Carousel>
  47. <Button
  48. onClick={props.previous}
  49. children='Previous'/>
  50. <Button
  51. onClick={props.next}
  52. children='Next'/>
  53. </div>
  54. )
  55. };
  56. // 我们通过在外面包裹一层container组件来给这个组件带来更多的功能.
  57. export default CarouselContainer(HeroCarousel)

通过保持样式和交互状态的分离, 基于同一个carsousel组件, 我们可以创造任意数量不同的更复杂的carousel组件,

用法

  1. const Carousel = () => (
  2. <div>
  3. <HeroCarousel />
  4. </div>
  5. );