react组件的几个模式

React Component Patterns

有状态组件 x 无状态组件、容器组件 x 展现组件、高阶组件 x Render Callbacks(Function as Child Component)react

使用React已经有一段时间了, React——Facebook库,使用JS构建用户界面。本文试图总结迄今为止实践中所学到的一些模式,同时但愿可以帮助到即将迈入奇妙地组件世界的开发者。git

react-pattern-post.png

有状态组件 X 无状态组件

正如web服务有静态和动态之分,React组件也有有状态和无状态的区分。有状态组件--在应用中组件能够拥有自身状态并操纵它;无状态组件--只接收属性进行效果呈现。web

一个简单的无状态组件,只受属性控制:编程

const Button = props => (
  <button onClick={props.onClick}>
    {props.text}
  </button>
);

一个具备计数功能的按钮组件(复用上面Button组件)小程序

class ButtonCounter extends React.Component {
  constructor() {
    super()
    this.state = { clicks: 0 }
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    this.setState({ clicks: this.state.clicks + 1 })
  }

  render() {
    return (
      <Button
        onClick={this.handleClick}
        text={`You've clicked me ${this.state.clicks} times!`}
      />
    )
  }
}

正如上面两个 Demo 所示,第二个组件的 constructor 具备状态的定义,第一个组件只是单纯地渲染属性文字。有状态组件和无状态组件的划分看起来很是简单,可是它对于组件复用具备重大意义。浏览器

容器组件 X 展现组件

当组件须要获取外部数据时,咱们又能够将组件划分为两种新的类型。容器组件负责获取数据,它经常是超出了React范畴的,如使用 Redux 或 Relay 进行了绑定。对比而言,展现型组件不依赖于程序其余部分,它只和自身状态或属性有关。下面咱们实现一个用户列表的展现组件:编程语言

const UserList = props =>
  <ul>
    {props.users.map(u => (
      <li>{u.name} — {u.age} years old</li>
    ))}
  </ul>

容器组件能够用来更新用户列表的展现:函数

class UserListContainer extends React.Component {
  constructor() {
    super()
    this.state = { users: [] }
  }

  componentDidMount() {
    fetchUsers(users => this.setState({ users }))
  }

  render() {
    return <UserList users={this.state.users} />
  }
}

这种分类将数据获取和渲染的逻辑分开,进而使用户列表组件能够复用。post

若是你想了解更多该模式的信息,awesome article from Dan Abramov 对它进行了精确的解释。学习

高阶组件 -- HOCs

当你想复用组件逻辑时,高阶组件很是有用。高阶组件--是将组件做为参数并返回新组件的 JS 函数

假设你须要构建一个可扩展菜单组件,当用户点击时,它会显示隐藏子组件内容。所以,你可使用高阶组件来实现:

function makeToggleable(Clickable) {
  return class extends React.Component {
    constructor() {
      super()
      this.toggle = this.toggle.bind(this)
      this.state = { show: false }
    }

    toggle() {
      this.setState(prevState => ({ show: !prevState.show }))
    }

    render() {
      return (
        <div>
          <Clickable
            {...this.props}
            onClick={this.toggle}
          />
          {this.state.show && this.props.children}
        </div>
      )
    }
  }
}

这种方法容许咱们使用 ES7装饰器语法将逻辑应用于 ToggleableMenu 组件:

@makeToggleable
class ToggleableMenu extends React.Component {
  render() {
    return (
      <div onClick={this.props.onClick}>
        <h1>{this.props.title}</h1>
      </div>
    )
  }
}

如今,咱们能够将任何子组件传递给ToggleableMenu组件:

class Menu extends React.Component {
  render() {
    return (
      <div>
        <ToggleableMenu title="First Menu">
          <p>Some content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Second Menu">
          <p>Another content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Third Menu">
          <p>More content</p>
        </ToggleableMenu>
      </div>
    )
  }
}

若是你熟悉 Redux 的 connect函数或者 React Router 的withRouter函数,那么你已经使用太高阶组件了。

渲染回调 -- Render Callbacks(Function as Child Components)

另外一个比较高端的复用组件逻辑的方法是将函数做为组件的 props.children,该方法也称为Function as Child Components。咱们将使用渲染回调来从新实现上面的可扩展Menu:

class Toggleable extends React.Component {
  constructor() {
    super()
    this.toggle = this.toggle.bind(this)
    this.state = { show: false }
  }

  toggle() {
    this.setState(prevState => ({ show: !prevState.show }))
  }

  render() {
    return this.props.children(this.state.show, this.toggle)
  }
}

如今,咱们能够将函数做为组件的子级进行传递:

<Toggleable>
    {(show, onClick) => (
      <div>
        <div onClick={onClick}>
          <h1>{props.title}</h1>
        </div>
        {show && props.children}
      </div>
    )}
  </Toggleable>

上面的代码已经将一个函数做为子组件,可是,若咱们想复用上述逻辑,咱们须要建立一个转换逻辑的新组件:

const ToggleableMenu = props =>
  <Toggleable>
    {(show, onClick) => (
      <div>
        <div onClick={onClick}>
          <h1>{props.title}</h1>
        </div>
        {show && props.children}
      </div>
    )}
  </Toggleable>

咱们使用Render Callbacks实现的可扩展的Menu组件以下:

class Menu extends React.Component {
  render() {
    return (
      <div>
        <ToggleableMenu title="First Menu">
          <p>Some content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Second Menu">
          <p>Another content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Third Menu">
          <p>More content</p>
        </ToggleableMenu>
      </div>
    )
  }
}

Render Callbacks和高阶函数使咱们的组件更加灵活,掌握和适应起来具备必定的难度,须要反复学习和消化。


本人能力有限,若有纰漏,请指正。

【开发环境推荐】 Cloud Studio 是基于浏览器的集成式开发环境,支持绝大部分编程语言,包括 HTML五、PHP、Python、Java、Ruby、C/C++、.NET 小程序等等,无需下载安装程序,一键切换开发环境。 Cloud Studio提供了完整的 Linux 环境,而且支持自定义域名指向,动态计算资源调整,能够完成各类应用的开发编译与部署。
相关文章
相关标签/搜索