1.mixins
写过 react 项目的应该都碰到过, 不同组件复用相同代码的问题, 在 react 早期使用 React.createClass 创建组件的时代, 我们经常使用的是 mixins 来实现代码复用比如有个组件 A, 它用来实时的获取鼠标的位置
- //A 组件
- import React from 'react'
- import ReactDOM from 'react-dom'
- const App = React.createClass({getInitialState() {
- return { x: 0, y: 0 }
- },
- handleMouseMove(event) {
- this.setState({
- x: event.clientX,
- y: event.clientY
- })
- },
- render() {
- const { x, y } = this.state
- return (
- <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
- <h1>The mouse position is ({x}, {y})</h1>
- </div>
- )
- }
- })
- ReactDOM.render(<App/>, document.getElementById('app'))
如果此时有个组件 B 也想集成这个功能, 我们可以通过 mixins, 代码是这样的
- //B 组件
- import React from 'react'
- import ReactDOM from 'react-dom'
- const MouseMixin = {
- getInitialState() {
- return { x: 0, y: 0 }
- },
- handleMouseMove(event) {
- this.setState({
- x: event.clientX,
- y: event.clientY
- })
- }
- }
- const App = React.createClass({
- // Use the mixin!
- mixins: [ MouseMixin ],
- render() {
- const { x, y } = this.state
- return (
- <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
- <h1>The mouse position is ({x}, {y})</h1>
- </div>
- )
- }
- })
- ReactDOM.render(<App/>, document.getElementById('app'))
很容易是吧~ 但委屈的是 react16 之后就不再支持 mixins 了, 因为 es6 普及了呀!
那怎么办呢? 办法总是有的, HOC(高阶组件) 的概念被提出来, 什么是高阶组件? 说白了其实就是把函数当做参数传入到另一个函数中, 在我们展开高阶组件前, 我们先来想想除了因为 es6 的普及 react 不再支持 mixins 之外, mixins 还有啥其它缺点不当然是有的, 有以下几点:
难以溯源, mixins 修改 state, 在组件内部你无法知道 state 从哪来, 尤其是有多个 mixins 的情况
命名空间, 多个 mixins 修改同一个 state 导致的命名冲突
2.HOC(高阶组件)
所以为了代替 mixins, 很多人就提出了 HOC(高阶组件), 代码是下面这样的
- import React from 'react'
- import ReactDOM from 'react-dom'
- const withMouse = (Component) => {
- return class extends React.Component {
- state = { x: 0, y: 0 }
- handleMouseMove = (event) => {
- this.setState({
- x: event.clientX,
- y: event.clientY
- })
- }
- render() {
- return (
- <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
- <Component {...this.props} mouse={this.state}/>
- </div>
- )
- }
- }
- }
- class App extends React.Component{
- render() {
- // 代替直接处理 state, 我们从 props 里获得 x,y 坐标
- const { x, y } = this.props.mouse
- return (
- <div style={{ height: '100%' }}>
- <h1>The mouse position is ({x}, {y})</h1>
- </div>
- )
- }
- }
- // 把 App 组件当做参数传到 withMouse 方法里面, 在 withMouse 内部通过 props 获得 xy 坐标值
- const AppWithMouse = withMouse(App)
- ReactDOM.render(<AppWithMouse/>, document.getElementById('app'))
看起来很不错的样子!
但是, 回到之前 mixins 存在的问题, 我们想一想, HOC 有上述的问题么? 我们来看下:
难以溯源, 跟 mixins 不同的是, 我们不再纠结 state 的源头, 我们现在要纠结的是 HOC 的 props 里提供了些啥...
命名空间的冲突, 这个问题依然存在, props 里属性名可能会被多个 HOC 重复使用
我的天.....
3.Render Prop
幸运女神降临! mmp, 前面都是炮灰, 到了划重点的时候啦!
Render Prop 是个值为函数的属性, 通过 Render Prop, 组件知道什么应该被渲染
很糊涂是不是, 看代码
- import React from 'react'
- import ReactDOM from 'react-dom'
- import PropTypes from 'prop-types'
- class Mouse extends React.Component {
- static propTypes = {
- render: PropTypes.func.isRequired
- }
- state = { x: 0, y: 0 }
- handleMouseMove = (event) => {
- this.setState({
- x: event.clientX,
- y: event.clientY
- })
- }
- render() {
- return (
- <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
- {this.props.render(this.state)}
- </div>
- )
- }
- }
- const App = React.createClass({
- render() {
- return (
- <div style={{ height: '100%' }}>
- <Mouse render={({ x, y }) => (
- <h1>The mouse position is ({x}, {y})</h1>
- )}/>
- </div>
- )
- }
- })
- ReactDOM.render(<App/>, document.getElementById('app'))
看明白了么, 这里我们通过定义一个 render 属性, 值是个函数, 描述了我们想要渲染的元素, 然后在子组件里面调用该 render 方法, 再回头看下之前的两个问题, 难以溯源, 现在主动权在父组件上, 我要什么数据你们给我拿来就行了, 你们子组件各自去实现, 我只要结果不要过程, 因而就不存在数据来源问题, 命名空间的问题也没了好厉害~~~ 最后偷偷的告诉你们一个更厉害的, 上面的 render 方法里面我们是直接写出了渲染 x,y 值, 只适用于当前 App 组件, 我们可以通过高阶组件来达到为任何组件添加该功能, 代码是这样的
- const withMouse = (Component) => {
- return class extends React.Component{
- render() {
- return <Mouse render={mouse=>(
- <Component {...this.props} mouse={mouse}/>
- )}/>
- }
- }
- }
据说 react-router 源码里面为每个组件增加路由属性就是通过该方法!
来源: https://juejin.im/post/5a965ac6f265da4e7a78889d