我似乎无法修复的无效钩子调用

时间:2021-02-10 00:05:56

标签: javascript reactjs

title 说的很清楚,我看过一些例子,但没有一个修复对我真正有用。我知道它是常量,将它移到类中并移出导出,没有运气。似乎无法让它工作。有人有任何想法吗?谢谢。

我在行 const [ws, setWs] = useState();

处收到以下错误

“错误:无效的钩子调用。钩子只能在函数组件的主体内部调用。这可能是由于以下原因之一:

  1. 您的 React 和渲染器版本可能不匹配(例如 React DOM)
  2. 你可能违反了钩子规则
  3. 您可能在同一个应用中拥有多个 React 副本"

export default function PlayerHoc(ComposedComponent) {
  const [ws, setWs] = useState();
  const [roomIp, setRoomIp] = useState();
  
  useEffect(() => {
    const wsUrl =
      process.env.NODE_ENV == "development"
        ? "ws://localhost:8888"
        : "ws://" + roomIp;
    setWs(new WebSocket(wsUrl));
  }, [roomIp]);
  
  useEffect(() => {
    if (!ws) return;
    ws.onopen = () => {
      ws.send("PlaybackRequest");
    };
  }, [ws]);


  class PlayerHoc extends Component {
        shouldComponentUpdate(nextProps) {
      return nextProps.playing || (this.props.playing && !nextProps.playing);
    }

    componentDidUpdate(prevProps) {
      if (prevProps.currentSong.id !== this.props.currentSong.id) {
        const id = this.props.currentSong.id;
        const other = this.props.currentSong.linked_from
          ? this.props.currentSong.linked_from.id
          : null;
        this.props.containsCurrentSong(other ? `${id},${other}` : id);
      }
    }

    render = () => (
      <ComposedComponent
        {...this.props}
        playContext={(context, offset) => this.props.playSong(context, offset)}
        playSong={() => this.props.playSong()}
        {...setRoomIp(this.props.roomIp)}
      />
    );
  }
//testbug
  const mapStateToProps = state => {
    return {
      currentSong: state.playerReducer.status
        ? state.playerReducer.status.track_window.current_track
        : {},
      contains: state.libraryReducer.containsCurrent ? true : false,
      trackPosition: state.playerReducer.status
        ? state.playerReducer.status.position
        : 0,
      playing: state.playerReducer.status
        ? !state.playerReducer.status.paused
        : false
    };
  };


  function nextSong(skip) {
    ws.send(JSON.stringify({ type: "skipSong", data: skip }))
  }
  function previousSong(prev) {
    ws.send(JSON.stringify({ type: "previousSong", data: prev }))
  }
  function pauseSong(pause) {
    ws.send(JSON.stringify({ type: "pauseSong", data: pause }))
  }
  function playSong(play) {
    ws.send(JSON.stringify({ type: "playSong", data: play }))
  }
  function seekSong(seek) {
    ws.send(JSON.stringify({ type: "seekSong", data: seek }))
  }

  const mapDispatchToProps = dispatch => {
    return bindActionCreators(
      {
        nextSong,
        previousSong,
        pauseSong,
        playSong,
        seekSong,

      },
      dispatch
    );
  };

   return connect(mapStateToProps,mapDispatchToProps)(PlayerHoc);
}

1 个答案:

答案 0 :(得分:1)

好吧,这是一团糟。但是让我们尝试将其重构为一个有效的高阶组件。

这里有几个问题,但主要是:

  • 在功能组件内定义类组件
  • 钩子使用不当。

让我们从定义一个普通的高阶组件开始。让我们称之为 withPlayer。

withPlayer 将返回一个 Class 组件。

在这个类组件中,我们可以做一些事情,比如创建一个 websocket,并构建你所有的播放器控件。

然后我们可以将这些播放器控件作为道具传递给 Wrapped 组件。

最后,我们的默认导出将应用我们的 redux connect HOC。我们可以使用 redux 中的 compose 函数来组合 withPlayer 并连接到我们的 Wrapped 组件。

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';

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

      const wsUrl =
        process.env.NODE_ENV == 'development' ? 'ws://localhost:8888' : 'ws://' + props.roomIp;

      this.ws = new WebSocket(wsUrl);
    }

    shouldComponentUpdate(nextProps) {
      return nextProps.playing || (this.props.playing && !nextProps.playing);
    }

    componentDidUpdate(prevProps) {
      if (prevProps.currentSong.id !== this.props.currentSong.id) {
        const id = this.props.currentSong.id;
        const other = this.props.currentSong.linked_from
          ? this.props.currentSong.linked_from.id
          : null;
        this.props.containsCurrentSong(other ? `${id},${other}` : id);
      }
    }

    nextSong = (skip) => {
      this.ws.send(JSON.stringify({ type: 'skipSong', data: skip }));
    };

    previousSong = (prev) => {
      this.ws.send(JSON.stringify({ type: 'previousSong', data: prev }));
    };

    pauseSong = (pause) => {
      this.ws.send(JSON.stringify({ type: 'pauseSong', data: pause }));
    };

    playSong = (play) => {
      this.ws.send(JSON.stringify({ type: 'playSong', data: play }));
    };

    seekSong = (seek) => {
      this.ws.send(JSON.stringify({ type: 'seekSong', data: seek }));
    };

    render() {
      return (
        <WrappedComponent
          {...this.props}
          playContext={(context, offset) => this.playSong(context, offset)}
          nextSong={this.nextSong}
          previousSong={this.previousSong}
          pauseSong={this.pauseSong}
          playSong={this.playSong}
          seekSong={this.seekSong}
        />
      );
    }
  };
}

const mapStateToProps = (state) => {
  return {
    currentSong: state.playerReducer.status
      ? state.playerReducer.status.track_window.current_track
      : {},
    contains: state.libraryReducer.containsCurrent ? true : false,
    trackPosition: state.playerReducer.status ? state.playerReducer.status.position : 0,
    playing: state.playerReducer.status ? !state.playerReducer.status.paused : false,
  };
};

export default compose(withPlayer, connect(mapStateToProps));


这就是你将如何使用它

import withPlayer from './withPlayer'

const MyComponent = props => {
  return <>You're Player wrapped component</>
}

export default withPlayer(Mycomponent);