使用 react hooks 进行全局状态管理时如何将状态从类组件传递到全局状态

时间:2021-02-21 23:38:16

标签: reactjs

对于我在 React 中构建的网络应用程序,我需要录制音频并能够以某种方式将录制的音频置于应用程序的全局状态中,以便我可以在应用程序的不同组件中使用和操作录制的音频。< /p>

我的全局状态是使用 React Hooks 设置的(使用 useReducer、createContext、useContext 创建和管理),我相信 Hooks 只适用于功能组件,而不适用于类组件。

所以我遇到的问题是,我为使浏览器麦克风正常工作而遵循的每个教程都使用类组件(如下面的代码),而不是功能组件。我假设这是有充分理由的,因为当我尝试将这些类组件转换为功能组件时,我收到错误消息:“无法读取 'undefined' 的属性 'finish'”

有没有办法获取这个音频数据 (blobURL) 并将其传递给我的全局状态?

或者(理想情况下),有没有办法使用麦克风在功能组件而不是类组件中录制音频?

import MicRecorder from "mic-recorder-to-mp3";
import React from "react";

const Mp3Recorder = new MicRecorder({ bitRate: 128 });

class AudioRecorder extends React.Component {
  constructor(props) {
    super(props);
    window.AudioContext = window.AudioContext || window.webkitAudioContext;

    this.state = {
      isRecording: false,
      isPaused: false,
      blobURL: "",
      isBlocked: false
    };
  }

  startRecording = () => {
    if (this.state.isBlocked) {
      console.log("Please give permission for the microphone to record audio.");
    } else {
      Mp3Recorder.start()
        .then(() => {
          this.setState({ isRecording: true });
        })
        .catch(e => console.error(e));
    }
  };

  stopRecording = () => {
    this.setState({ isRecording: false });
    Mp3Recorder.stop()
      .getMp3()
      .then(async ([buffer, blob]) => {
        const blobURL = URL.createObjectURL(blob)
        this.setState({ 
          blobURL: blobURL,
          isRecording: false
        });
      })
      .catch(e => console.log(e));
  };

  checkPermissionForAudio = () => {
    if (navigator.mediaDevices === undefined) {
      navigator.mediaDevices = {};
    }
    if (navigator.mediaDevices.getUserMedia === undefined) {
      navigator.mediaDevices.getUserMedia = function(constraints) {
        // First get ahold of the legacy getUserMedia, if present
        var getUserMedia =
          // navigator.getUserMedia ||
          navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

        // Some browsers just don't implement it - return a rejected promise with an error
        // to keep a consistent interface
        if (!getUserMedia) {
          return Promise.reject(
            new Error("getUserMedia is not implemented in this browser")
          );
        }

        // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
        return new Promise(function(resolve, reject) {
          getUserMedia.call(navigator, constraints, resolve, reject);
        });
      };
    }
    navigator.mediaDevices
      .getUserMedia({ audio: true })
      .then(stream => {
        this.setState({ isBlocked: false });
      })
      .catch(err => {
        this.setState({ isBlocked: true });
        console.log("Please give permission for the microphone to record audio.");      
        console.log(err.name + ": " + err.message);
      });
  };

  componentDidMount() {
    this.checkPermissionForAudio();
  }

  render() {
    const { isRecording } = this.state;
    return (
      <React.Fragment>
        <button
          onClick={this.startRecording}
          className="mr-3 add-collec-btn"
          disabled={isRecording}
        >
          Record
        </button>
        <button
          onClick={this.stopRecording}
          className="mr-3 delete-btn"
          disabled={!isRecording}
        >
          Stop
        </button>
        <audio
          ref="audioSource"
          controls="controls"
          src={this.state.blobURL || ""}
        />
      </React.Fragment>
    );
  }
}

export default AudioRecorder;

更新: 这就是我如何在我的应用程序中设置 Context 以及它是如何通过代码提供的。在我的商店文件夹中,我有三个文件:Context.js、GlobalStateProvider 和 useGlobalState。

Context.js

import { createContext } from 'react';

const Context = createContext({});

export default Context;

GlobalStateProvider.js

这包装了我的 App.js 文件中的所有内容

import React from 'react';
import useGlobalState from './useGlobalState';
import Context from './Context';

const GlobalStateProvider = ({ children }) => {
    return (
        <Context.Provider value={useGlobalState()}>{children}</Context.Provider>
    );
}

export default GlobalStateProvider;

使用GlobalState.js

import { useReducer } from 'react';

const reducer = (state, action) => {
    switch (action.type) {
        case 'SETISRECORD':
            return {
                ...state,
                isRecording: action.payload
            }
        case 'SETISBLOCKED':
            return {
                ...state,
                isBlocked: action.payload
            }
        case 'setBlobURL':
            return {
                ...state,
                blobURL: action.payload
            }
        default: {
            return state;
        }
    }
};

const useGlobalState = () => {
    const [globalState, globalDispatch] = useReducer(reducer, {
        isRecording: false,
        isBlocked: false,
        blobURL: '',
    });

    return { globalState, globalDispatch };
}

export default useGlobalState;

然后我在功能组件中与我的全局状态交互,如下所示:

const functionalComponent = () => {  
    const { globalState, globalDispatch } = useContext(Context);

    return (
      [code]
    );
}

1 个答案:

答案 0 :(得分:1)

基于类的组件仍然可以“使用”上下文,但语法比简单地使用 useContext React 钩子要复杂一些。

Context.Consumer

对于您的情况,您将导入全局状态上下文 Context 并呈现需要通过子函数访问上下文的组件。然后子组件需要通过 props 使用这些上下文值。

一些基于分类的组件:

class MyComponent extends React.Component {
  ...

  render() {
    const { myContext: { globalState, globalDispatch } } = this.props;
    return (
      [code]
    );
  }
}

通过 props 包裹和传递:

import MyContext from '../path/to/context';

...

<MyContext.Consumer>
  {myContext => <MyComponent myContext={myContext} />}
</MyContext.Consumer>