網站搜尋

javascript - 在函數式組件中,我可以使用什麼來獲得與 componentDidMount 相同的行為?


我的使用者介面運作正常,直到它使用了一個類別元件。現在我正在將它重構為函式元件。

我必須根據從 API 處理器收到的資料來加載我的使用者介面。我的使用者介面將反映存在於房間內的攝影機的狀態。每次攝影機在房間內開啟或關閉時,我都應該從 API apiToGetCameraState 接收到新的狀態。

我希望位於 registerVideoStateUpdateHandlerWrapper 內的 console.log 能在介面第一次加載時以及每次房間中的影片狀態改變時都能被觸發。然而,在介面第一次加載時,它並沒有運作。

這就是我的組件的樣子:

const Home: React.FunctionComponent<{}> = React.memo(() => {
  const [video, setToggleVideo] = React.useState(true);

  const registerVideoStateUpdateHandlerWrapper = React.useCallback(() => {
    apiToGetCameraState(
      (videoState: boolean) => {
        // this log does not show up when the UI is loaded for the first time
        console.log(
          `Video value before updating the state: ${video} and new state is: ${videoState} `
        );
        setToggleVideo(videoState);
      }
    );
  }, [video]);

  React.useEffect(() => {
    //this is getting called when the app loads
    alert(`Inside use effect for Home component`);
    registerVideoStateUpdateHandlerWrapper ();
  }, [registerVideoStateUpdateHandlerWrapper ]);

  return (
      <Grid>
        <Camera
          isVideoOn={video}
        />
      </Grid>
  );
});

當我的程式碼在類別元件中時,這運作得很好。類別元件長這樣。

class Home extends Component {
    
    registerVideoStateUpdateHandlerWrapper = () => {
        apiToGetCameraState((videoState) => {
            console.log(`ToggleVideo value before updating the state: ${this.state.toggleCamera} and new state is: ${videoState}`);
            this.setStateWrapper(videoState.toString());
        })
    }
    setStateWrapper = (toggleCameraUpdated) => {
        console.log("Inside setStateWrapper with toggleCameraUpdated:" + toggleCameraUpdated);
        this.setState({
            toggleCamera: (toggleCameraUpdated === "true" ) ? "on" : "off",
        });
    }
   
    constructor(props) {
        super(props);
        this.state = {
            toggleCamera: false,
        };
    }
    componentDidMount() {
        console.log(`Inside componentDidMount with toggleCamera: ${this.state.toggleCamera}`)
        this.registerVideoStateUpdateHandlerWrapper ();   
    }
    render() {
      return (
        <div>
          <Grid>
            <Camera isVideoOn={this.state.toggleCamera} />
          </Grid>
     );
  }
}

我嘗試了哪些事情?

  1. 我嘗試在 registerVideoStateUpdateHandlerWrapper 函數中移除 useCallback,並且也從 React.useEffect 和 registerVideoStateUpdateHandlerWrapper 中移除依賴陣列。它的行為還是一樣。
  2. 我試著更新 React.useEffect,將 registerVideoStateUpdateHandlerWrapper 的程式碼放進去,但仍然沒有成功。

答案#1

將 registerVideoStateUpdateHandlerWrapper() 移動到 useEffect() 回呼函數內,像這樣。如果你想在狀態變更時記錄先前的狀態,你應該使用函數型更新,以避免透過閉包捕捉到先前的狀態:

const Home = () => {
  const [video, setVideo] = useState(false);

  useEffect(() => {
    console.log('Inside useEffect (componentDidMount)');

    const registerVideoStateUpdateHandlerWrapper = () => {
      apiToGetCameraState((videoState) => {
        setVideo((prevVideo) => {
          console.log(`Video value before updating the state: ${prevVideo} and new state is: ${videoState}`);
          return videoState;
        });
      });
    };

    registerVideoStateUpdateHandlerWrapper();
  }, []);

  return (
    <Grid>
      <Camera isVideoOn={video} />
    </Grid>
  );
};

當你實際上不再需要記錄先前的狀態時,你應該將 registerVideoStateUpdateHandlerWrapper() 簡化為:

const registerVideoStateUpdateHandlerWrapper = () => { 
  apiToGetCameraState((videoState) => {
    setVideo(videoState);
  });
};

答案#2

import React from 'react'

const Home = () => {
    const [video, setVideo] = useState(null);
    //default video is null, when first load video will change to boolean, when the Camera component will rerender
    const registerVideoStateUpdateHandlerWrapper = () => {
        apiToGetCameraState((videoState) => {
            setVideo(videoState);
        });
    };

    useEffect(() => {
      registerVideoStateUpdateHandlerWrapper();
    }, []);
  
    return (
      <Grid>
        <Camera isVideoOn={video} />
      </Grid>
    );
  };

  export default Home