将HOC包装器组件更改为其自身的状态以响应钩子

时间:2019-05-28 23:21:35

标签: reactjs react-hooks

我目前有一个HOC组件,我想移植它以使用react挂钩,并且基本上只是开始考虑这个想法。

此HOC组件基本上为包装的组件提供了显示警报对话框的功能。 HOC组件管理自己的状态,这使得包装的组件非常容易显示警报对话框。包装的组件只需调用传递给props的函数来显示它。

HOC现在是这样的:

function withAlertDialog(WrappedComponent) {
  return class extends Component {
    constructor(props) {
      super(props);

      this.state = {
        alertDialogOpen: false,
        alertMessage: ""
      };
    }

    displayAlert = message => {
      this.setState({alertDialogOpen: true, alertMessage: message});
    }

    closeAlertDialog = () => {
      this.setState({alertDialogOpen: false});
    }

    render() {
      return (
        <React.Fragment>
          <WrappedComponent 
            onDisplayAlert={this.displayAlert}
            onCloseAlert={this.closeAlertDialog} />
          <MyAlertDialogComponent
            open={this.state.alertDialogOpen}
            onClose={this.closeAlertDialog} />
        </React.Fragment>
      );
    }
  }
}

这是一个更简单的情况,实际使用的HOC要复杂得多,但是这个想法仍然存在。现在,已包装的组件基本上可以调用this.props.onDisplayAlert('some message here');来显示警报。包装的组件也不必在自己的渲染函数中渲染MyAlertDialogComponent。基本上,包装的组件不必担心MyAlertDialogComponent的处理方式,它所知道的就是调用this.props.onDisplayAlert会以某种方式显示一个警告对话框。重用此HOC可以节省很多代码。

如何将其更改为react hooks实现?我已经尝试过环顾四周,但是大多数文章和文档本身使用的是带有单个包装组件的HOC,除此之外,实际上并没有管理其他组件。我想了解如何更改为“反应挂钩”的思想,但保持相同的方便程度,不必在要使用它的每个组件中呈现MyAlertDialogComponent

1 个答案:

答案 0 :(得分:0)

旧的HOC和使用钩子的新HOC之间的唯一区别是,您只需要将从HOC返回的匿名类更改为使用钩子的匿名函数。

类和挂钩函数之间的转换遵循正常的转换规则,您可能会在许多在线教程中找到这些规则。以您的示例为例,将状态转换为useState并将类方法转换为常规函数。

您只需将状态和这些常规功能传递给需要它们的任何组件。调用您所在州的setter将会重新渲染该组件。

如果您查看下面的示例,您将看到MyWrappedComponentwithAlertDialog包装,该包装将两个功能道具传递给MyWrappedComponent。这些功能在MyWrappedComponent内部使用,以设置呈现MyAlertDialogComponent

的状态

const { useState } = React
function withAlertDialog(WrappedComponent) {
  return function(props){
    const [alertState, setAlertState] = useState({
      alertDialogOpen: false,
      alertMessage: ""
    })

    const displayAlert = message => {
      setAlertState({
        alertDialogOpen: true, 
        alertMessage: message
      });
    }

    const closeAlertDialog = () => {
      setAlertState({alertDialogOpen: false});
    }
    
    return (
      <React.Fragment>
        <WrappedComponent 
          onDisplayAlert={displayAlert}
          onCloseAlert={closeAlertDialog} />
        <MyAlertDialogComponent
          open={alertState.alertDialogOpen}
          onClose={closeAlertDialog} />
      </React.Fragment>
    );
  }
}

const MyWrappedComponent = withAlertDialog(function (props){
  return (
    <div>
      <a onClick={props.onDisplayAlert}>Open Alert</a>
      <a onClick={props.onCloseAlert}>Close Alert</a>
    </div>
  )
})

function MyAlertDialogComponent(props){
  if(!props.open){
    return null
  }
  return (
    <div>Dialogue Open</div>
  )
}

function App(){
  return (
    <MyWrappedComponent />
  )
}

ReactDOM.render(<App />, document.querySelector('#app'))
div > a {
 display : block;
 padding : 10px 0;
}
<div id="app" />

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>