使用自己的状态更改 HOC 包装器组件以响应挂钩

Change HOC wrapper component with its own state to react hooks

我目前有一个 HOC 组件,我想移植它以使用 React Hooks,基本上只是开始思考这个想法。

这个 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,除此之外并没有真正管理另一个组件。我想了解如何更改为 "react hooks" 意识形态,但保持相同的便利程度,即不必在每个想要使用它的组件中呈现 MyAlertDialogComponent

您的旧 HOC 和使用挂钩的新 HOC 之间的唯一区别是您只需将您 return 的匿名 class 从您的 HOC 更改为使用挂钩的匿名函数。

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

您只需将状态和这些常规函数传递给任何需要它们的组件。为您的州调用 setter 将 re-render 组件。

如果您查看下面的示例,您会看到 MyWrappedComponent 是使用 withAlertDialog 包装的,它将两个函数属性传递给 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>